diff --git a/README.md b/README.md index 43ecc07..565a2fb 100644 --- a/README.md +++ b/README.md @@ -7,15 +7,25 @@ # 经典排序算法 -- [冒泡排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) -- [选择排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) -- [插入排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) -- [归并排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) -- [快速排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) -- [希尔排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) -- [桶排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E6%A1%B6%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) -- [基数排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) -- [堆排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E5%A0%86%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) +✅ [冒泡排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) + +✅ [选择排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) + +✅ [插入排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) + +✅ [插入排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) + +✅ [归并排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) + +✅ [快速排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) + +✅ [希尔排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) + +✅ [桶排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E6%A1%B6%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) + +✅ [基数排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) + +✅ [堆排序](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/algorithms/%E5%A0%86%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md) ## 排序算法总结 @@ -23,15 +33,21 @@ | :--: | :------: | :----------: | :--: | :--: | :-----------------------: | :--: | | 冒泡排序 | O(n^2) | O(1) | | | | 稳定 | | 插入排序 | O(n^2) | O(1) | | | | 稳定 | -| 计数排序 | | O(M) | | | 对于用例少的数据,比如对人的年龄排序或者身高排序。 | 稳定 | +| 计数排序 | O(n + k) | O(M) | | | 对于用例少的数据,比如对人的年龄排序或者身高排序。 | 稳定 | | 基数排序 | O(d(n+r)) | O(M) | | | | 稳定 | -| 桶排序 | | | | | | 稳定 | +| 桶排序 | O(n+k) | O(n + k) | | | | 稳定 | | 选择排序 | O(n^2) | O(1) | | | | 不稳定 | | 归并排序 | O(nlogn) | O(N) | | | | 稳定 | | 快速排序 | O(nlogn) | O(logn)~O(n) | | | | 不稳定 | | 希尔排序 | O(nlogn) | O(1) | | | | 不稳定 | | 堆排序 | O(nlogn) | O(1) | | | | 不稳定 | +## 其他算法总结 + +- 二分查找法,时间复杂度为O(logn) + +## 其他 + 注意这里总结的都是平均时间复杂度。 例如插入排序,如果是有序的数组,则时间复杂度会退化成O(n)。而快速排序,对于每次选择的p位置都是末尾位置,则会退化成时间复杂度为O(n^2)(通过让p的位置随机来解决这个问题)。 @@ -39,40 +55,79 @@ 这里还需要注意的是,对于插入排序、快速排序、堆排序来说,都是原地排序的,即不需要额外的空间;而归并排序则是非原地排序,需要借助额外空间。 -> 排序算法的稳定性:对于相等的元素,在排序后,原来靠前的元素依然靠前,相等元素的相对位置没有发生改变。 +1. 排序算法的稳定性:对于相等的元素,在排序后,原来靠前的元素依然靠前,相等元素的相对位置没有发生改变。 +2. 对于算法面试来说,除非面试题特别说明,否则认为要排序的数据范围是均匀分布的。 +3. 快速排序之所以叫快速排序,并不代表它比堆排序和归并排序优良。在最好情况下,它的渐进复杂度与 堆排序和归并排序是相同的。知识快速排序的常量系数比较小而已。 +4. 类库上提供的排序,并不是某一种算法的实现,而是综合了多种排序的综合排序。当数组比较小时,使用插入排序;当数组较大时,选择快速排序或其他的O(nlogn)的排序。 -> 对于算法面试来说,除非面试题特别说明,否则认为要排序的数据范围是均匀分布的。 +# 经典算法 -> 快速排序之所以叫快速排序,并不代表它比堆排序和归并排序优良。在最好情况下,它的渐进复杂度与 堆排序和归并排序是相同的。知识快速排序的常量系数比较小而已。 -> 类库上提供的排序,并不是某一种算法的实现,而是综合了多种排序的综合排序。当数组比较小时,使用插入排序;当数组较大时,选择快速排序或其他的O(nlogn)的排序。 +KMP算法 + +马拉车算法 -# 经典算法 +Prim算法 +Krusk算法 -- KMP算法 -- 马拉车算法 -- Prim算法 -- Krusk算法 -- Dijkstra算法 -- Bellman-Ford算法 +Dijkstra算法 + +Bellman-Ford算法 # 经典数据结构 -- 数组 -- 栈和队列 -- 链表 -- 二分搜索树 -- 集合和映射 -- [堆和优先队列](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/datastructures/%E5%A0%86%E5%92%8C%E4%BC%98%E5%85%88%E9%98%9F%E5%88%97.md) 【更新中】 -- 线段树 -- Trie树 -- 并查集 -- [AVL树](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/datastructures/AVL%E6%A0%91.md)【更新中】 -- 红黑树 -- 哈希表 - -==================== 持续更新 =================== +数组 + +栈和队列 + +链表 + +二分搜索树 + +集合和映射 + +✅ [堆和优先队列](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/datastructures/%E5%A0%86%E5%92%8C%E4%BC%98%E5%85%88%E9%98%9F%E5%88%97.md) + +线段树 + +Trie树 + +并查集 + +✅ [AVL树](https://github.com/coderbruis/AlgorithmsInJava/blob/master/notes/datastructures/AVL%E6%A0%91.md) + +红黑树 + +哈希表 + +# 数据结构总结 + +线性结构和非线性结构数据结构的区别 + +> 线性结构 + +1. 线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系。 +2. 线性结构拥有两种不同的存储结构,即顺序存储结构和链式存储结构。顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的,链式存储的线性表称为链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息。 +3. 线性结构中存在两种操作受限的使用场景,即队列和栈。栈的操作只能在线性表的一端进行,就是我们常说的先进后出(FILO),队列的插入操作在线性表的一端进行而其他操作在线性表的另一端进行,先进先出(FIFO),由于线性结构存在两种存储结构,因 此队列和栈各存在两个实现方式。 +4. 常见的线性结构包括:数组、链表、堆栈和队列等。 + +> 非线性结构 + +1. 树作为一种应用广泛的一对多非线性数据结构,不仅有数据间的指向关系,还有层级关系,常见的有二分搜索树(二叉树)、AVL树、B树以及红黑树等。 + + + +| 名称 | 类型 | 特性 | 常见应用 | +| :----: | :----: | :----: | :----: | +| 数组 | 线性结构 | 1. 基础数据机构;底层通过索引快速取得元素值;
2. 查询快O(1),增删慢O(n); | JDK中的ArrayList | +| 链表 | 线性结构 | 1. 以链表节点为元素,元素中包含指向下一节点的引用;
2. 链表分为单向链表和双向链表,区别在于双向链表有前驱节点和后继节点;
3. 查询慢O(n),增删快O(1);| JDK中的LinkedList、LinkedHashMap等 | +| 栈 | 线性结构 | 1. 先进后出的数据结构;| JDK中的SynchronousQueue | +| 队列 | 线性结构 | 1. 先进先出的数据结构;| JDK中的线程池底层的队列,包括:LinkedBlockingQueue、SynchronousQueue、ArraysBlockingQueue、DelayQueue等 | +| 二分搜索树 | 非线性结构 | 1. 二分搜索树是一颗二叉树,每棵树节点都包含了节点值;
2. 每个节点值都大于左子树的所有节点的值,而小于右子树的所有节点的值;
| x | +| AVL树 | 非线性结构 | 1. AVL树是一个平衡二叉树,平衡二叉树的特性就是指左右加点的差值不能大于一;
2. AVL树每次插入、删除都需要进行左旋、右旋来保持AVL树的平衡性,因而这种维护需要更多代价;
3. AVL树的高度平衡,查找节点效率高;
4. AVL树的高度为logN;| windows对进程地址空间的管理用到了AVL树 | +| 红黑树 | 非线性结构 | 1. 1 根节点为黑色;2 红黑树节点要么是红色,要么是黑色;3 任意节点到叶子节点经过的黑色节点数量相同;4 每个叶子节点(没有子节点的节点)都是黑色的;5 如果一个节点是红色的,那么它的子节点都是黑色的;
2. 红黑树是"近似平衡",因为红色节点的存在,让红黑树能够实现红色节点和其父亲节点进行融合,从而成为类似于2-3树这种"绝对平衡的树";
3. 红黑树的高度近似于2logN;
4. 红黑树由于是近似平衡,所以维护成本比AVL树的要低,并且实践证明,红黑树的插入、查找和删除性能都比较稳定;| JDK中的TreeMap使用了红黑树;linux中的epoll底层,用红黑树来存储epoll_fd; | +| B+树 | 非线性结构 | 1. 多节点,层数低;2. 时间复杂度为O(logn); | InnoDB底层使用的B+树 | # leetcode专区 @@ -81,6 +136,9 @@ 题目名称 | 难度 | 地址 | 题解 ---|---|---|--- 两数之和 | 简单 | [https://leetcode-cn.com/problems/two-sum/](https://leetcode-cn.com/problems/two-sum/) | [题解](https://github.com/coderbruis/AlgorithmsInJava/blob/master/src/main/java/com/bruis/algorithminjava/algorithm/array/TwoSum.java) +两数之和 II - 输入有序数组 | 简单 | [https://leetcode-cn.com/problems/two-sum-ii/](https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/description/?utm_source=LCUS&utm_medium=ip_redirect_q_uns&utm_campaign=transfer2china) | [题解](https://github.com/coderbruis/AlgorithmsInJava/blob/master/src/main/java/com/bruis/algorithminjava/algorithm/array/TwoSumII.java) +反转字符串中的元音字母 | 简单 | [https://leetcode-cn.com/problems/reverse-vowels-of-a-string/](https://leetcode-cn.com/problems/reverse-vowels-of-a-string/) | [题解](https://github.com/coderbruis/AlgorithmsInJava/blob/master/src/main/java/com/bruis/algorithminjava/algorithm/array/ReverseVowels.java) +验证回文串 | 简单 | [https://leetcode-cn.com/problems/valid-palindrome/](https://leetcode-cn.com/problems/valid-palindrome/) | [题解](https://github.com/coderbruis/AlgorithmsInJava/blob/master/src/main/java/com/bruis/algorithminjava/algorithm/array/IsPalindrome.java) 三数之和 | 中等 | [https://leetcode-cn.com/problems/3sum/](https://leetcode-cn.com/problems/3sum/) | [题解](https://github.com/coderbruis/AlgorithmsInJava/blob/master/src/main/java/com/bruis/algorithminjava/algorithm/array/ThreeSum.java) 乘积最大子数组 | 中等 | [https://leetcode-cn.com/problems/maximum-product-subarray/](https://leetcode-cn.com/problems/maximum-product-subarray/) | [题解](https://github.com/coderbruis/AlgorithmsInJava/blob/master/src/main/java/com/bruis/algorithminjava/algorithm/array/MaximumProductSubarray.java) 和为K的子数组 | 中等 | [https://leetcode-cn.com/problems/subarray-sum-equals-k/](https://leetcode-cn.com/problems/subarray-sum-equals-k/) | [题解](https://github.com/coderbruis/AlgorithmsInJava/blob/master/src/main/java/com/bruis/algorithminjava/algorithm/array/SubarraySumEqualsK.java) @@ -95,7 +153,6 @@ ## 3. 链表 -==================== 持续更新 =================== # 支持 diff --git "a/notes/algorithms/351円200円211円346円213円251円346円216円222円345円272円217円347円256円227円346円263円225円.md" "b/notes/algorithms/351円200円211円346円213円251円346円216円222円345円272円217円347円256円227円346円263円225円.md" index 7b247d7..82b1ae2 100644 --- "a/notes/algorithms/351円200円211円346円213円251円346円216円222円345円272円217円347円256円227円346円263円225円.md" +++ "b/notes/algorithms/351円200円211円346円213円251円346円216円222円345円272円217円347円256円227円346円263円225円.md" @@ -30,7 +30,7 @@ public class SelectionSort { for (int i = 0; i < n; i++) { int min = i; - for (int j = i+1; i < n; i++) { + for (int j = i+1; j < n; i++) { if (arr[j] < arr[min]) { min = j; } diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/huawei/Question01.java b/src/main/java/com/bruis/algorithminjava/algorithm/huawei/Question01.java new file mode 100644 index 0000000..6f1092a --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/huawei/Question01.java @@ -0,0 +1,54 @@ +package com.bruis.algorithminjava.algorithm.huawei; + +import java.util.Scanner; + +/** + * @author LuoHaiYang + * + * 题目描述:输入一个int型的正整数,计算出该int型数据在内存中存储时1的个数。 + * + * 输入描述: 输入一个整数(int类型) + * + * 输出描述: 这个数转换成2进制后,输出1的个数 + * + * 实例1: + * + * 输入:5 + * 输出:2 + * + */ +public class Question01 { + + /** + * 笨办法 + * @param args + */ + public static void main(String[] args) { + + // 巧妙法,通过二进制位运算 + Scanner in = new Scanner(System.in); + + int count = in.nextInt(), result = 0; + while (count> 0) { + if ((count & 1)> 0) { + result++; + } + count = count>> 1; + } + System.out.println(result); +/* + 笨办法 + Scanner scanner = new Scanner(System.in); + int input = scanner.nextInt(); + char[] bytes = Integer.toBinaryString(input).toCharArray(); + int result = 0; + for (char c : bytes) { + if (c == '1') { + result++; + } + } + System.out.println(result); +*/ + } + +} diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/ContainsDuplicate_217.java b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/ContainsDuplicate_217.java new file mode 100644 index 0000000..924d68c --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/ContainsDuplicate_217.java @@ -0,0 +1,111 @@ +package com.bruis.algorithminjava.algorithm.leetcode; + +import java.util.*; + +/** + * 217: Contains Duplicate + * @Description + * @Author luohaiyang + * @Date 2022年6月24日 + */ +public class ContainsDuplicate_217 { + + public static void main(String[] args) { + int[] test = {1, 2, 30, 22, 3, 4, 5, 1}; + System.out.println(containsDuplicate(test)); + } + + public static boolean containsDuplicate(int[] nums) { + // 数组类型题思考步骤: + // 1) 入参是否合法,是否越界等; + // 2) 是否是有序; + // 3) 是否有负数; + + // 题解思路: + // 1. 暴力破解法; O(n^2); + // 2. 桶排序? + // 2.1 需要Map数据结构; + // 2.2 不需要Map数据结构; + // 3. 排序之后,判断前后数字是否一样; + + return false; + } + + public static boolean setSolution(int[] nums) { + int n = nums.length; + if (n < 1) { + return false; + } + Set set = new HashSet(); + for (int x : nums) { + if (!set.add(x)) { + return true; + } + } + return false; + } + + /** + * 通过排序来进行筛选 + * @param nums + * @return + */ + public static boolean sortSolution(int[] nums) { + int n = nums.length; + if (n < 1) { + return false; + } + Arrays.sort(nums); + for (int i = 1; i < nums.length - 1; i++) { + if (nums[i] == nums[i+1]) { + return true; + } + } + return false; + } + + /** + * 基于桶排序 + * @param nums + * @return + */ + public static boolean bucketMapSolution(int[] nums) { + int n = nums.length; + if (n < 1) { + return false; + } + + Map duplicateMap = new HashMap(n); + + for (int i = 0; i < n; i++) { + if (!duplicateMap.containsKey(nums[i])) { + duplicateMap.put(nums[i], 1); + continue; + } + return true; + } + return false; + } + + /** + * 暴力解法 + * @param nums + * @return + */ + public static boolean violentSolution(int[] nums) { + if (nums.length < 1) { + return false; + } + + // 方法一:暴力解法 + int n = nums.length; + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (i != j && nums[i] == nums[j]) { + return true; + } + } + } + return false; + } +} diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/LongestPalindromicSubstring_5.java b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/LongestPalindromicSubstring_5.java new file mode 100644 index 0000000..2c84640 --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/LongestPalindromicSubstring_5.java @@ -0,0 +1,9 @@ +package com.bruis.algorithminjava.algorithm.leetcode; + +/** + * @Description + * @Author luohaiyang + * @Date 2022/6/28 + */ +public class LongestPalindromicSubstring_5 { +} diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/MaximumSubarray_53.java b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/MaximumSubarray_53.java new file mode 100644 index 0000000..f59db25 --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/MaximumSubarray_53.java @@ -0,0 +1,68 @@ +package com.bruis.algorithminjava.algorithm.leetcode; + +/** + * @Description + * @Author luohaiyang + * @Date 2022/6/24 + */ +public class MaximumSubarray_53 { + + public static void main(String[] args) { + // 条件: + // 1. 连续数组(不可以排序); + + // 解题思路: + // 1. 暴力解法;(超时) + // 2. 正确解法-动态规划; + + // 有卡顿、纠结的点: + // 1. 算法起始条件、结束条件; + + int[] nusm = {-2,1,-3,4,-1,2,1,-5,4}; + System.out.println(violentSolution(nusm)); + } + + public static int violentSolution(int[] nums) { + int n = nums.length; + if (n < 2) { + return nums[0]; + } + int max = nums[0];; + for (int i = 0; i < n; i++) { + int subTotal = nums[i]; + for (int j = i + 1; j < n; j++) { + if (max < nums[j]) { + max = nums[j]; + } + subTotal += nums[j]; + if (max < subTotal) { + max = subTotal; + } + } + } + return max; + } + + public static int maxSubArray(int[] nums) { + int n = nums.length; + if (n < 2) { + return nums[0]; + } + int max = nums[0]; + int i = 0, j = i + 1; + while (j < n) { + if (max < nums[j]) { + max = nums[j]; + } + int totalMax = 0; + for (int k = i; k < j; k++) { + totalMax += nums[i] + nums[j]; + } + if (max < totalMax) { + max = totalMax; + } + j++; + } + return max; + } +} diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/TwoSum.java b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/TwoSum.java new file mode 100644 index 0000000..c3dea2f --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/TwoSum.java @@ -0,0 +1,90 @@ +package com.bruis.algorithminjava.algorithm.leetcode; + +import java.util.HashMap; +import java.util.Map; + +/** + * @Description + * @Author luohaiyang + * @Date 2022/4/28 + */ +public class TwoSum { + public int[] twoSum(int[] nums, int target) { + // 暴力破解法 +// return forceSolution(nums, target); + return optimizeSolution01(nums, target); + } + + /** + * 对optimizeSolution01进行优化,少进行一次for循环 + * 时间复杂度: O(n) + * 空间复杂度:O(n) + * @param nums + * @param target + * @return + */ + private int[] optimizeSolution02(int[] nums, int target) { + int n = nums.length; + Map arrayMap = new HashMap(); + for (int i = 0; i < n; i++) { + int num = nums[i]; + if (arrayMap.containsKey(target - num)) { + return new int[]{arrayMap.get(target - num), i}; + } + arrayMap.put(num, i); + } + return new int[0]; + } + + /** + * 借助jdk hashmap, + * 时间复杂度: O(n) + * 空间复杂度:O(n) + * @param nums + * @param target + * @return + */ + private int[] optimizeSolution01(int[] nums, int target) { + int n = nums.length; + Map arrayMap = new HashMap(); + for (int i = 0; i < n; i++) { + int num = nums[i]; + int targetVal = target - num; + arrayMap.put(targetVal, i); + } + for (int i = 0; i < n; i++) { + int num = nums[i]; + if (arrayMap.containsKey(num)) { + int index = arrayMap.get(num); + if (index> i) { + return new int[]{i, index}; + } else { + return new int[]{index, i}; + } + } + } + return new int[0]; + } + + /** + * 暴力破解法 + * 时间复杂度:o(n^2) + * 空间复杂度:O(n) + * @param nums + * @param target + * @return + */ + private int[] forceSolution(int[] nums, int target) { + int n = nums.length; + for (int i = 0; i < n; i++) { + int num = nums[i]; + int val = target - num; + for (int j = i + 1; j < n; j++) { + if (nums[j] == val) { + return new int[]{i, j}; + } + } + } + return new int[0]; + } +} diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/TwoSumII.java b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/TwoSumII.java new file mode 100644 index 0000000..79147f1 --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/TwoSumII.java @@ -0,0 +1,62 @@ +package com.bruis.algorithminjava.algorithm.leetcode; + +/** + * @Description + * @Author luohaiyang + * @Date 2022/4/28 + */ +public class TwoSumII { + public int[] twoSum(int[] numbers, int target) { + return twoPointer(numbers, target); + } + + /** + * 双指针 + * 时间复杂度:O(n) + * 空间复杂度:O(1) + * @param numbers + * @param target + * @return + */ + private int[] twoPointer(int[] numbers, int target) { + int n = numbers.length; + if (n < 2) { + return numbers; + } + int i = 0, j = n - 1; + while (i < j) { + if (numbers[i] + numbers[j] == target) { + return new int[]{i + 1, j + 1}; + } + if (numbers[i] + numbers[j]> target) { + j--; + } else { + i++; + } + } + return new int[0]; + } + + /** + * 暴力法: + * 时间复杂度:O(n^2) + * 空间复杂度:O(1) + * @param numbers + * @param target + * @return + */ + private int[] forceSolution(int[] numbers, int target) { + int n = numbers.length; + if (n < 2) { + return numbers; + } + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (numbers[i] + numbers[j] == target) { + return new int[]{i + 1, j + 1}; + } + } + } + return new int[0]; + } +} diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/array/IsPalindrome.java b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/array/IsPalindrome.java new file mode 100644 index 0000000..d75488f --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/array/IsPalindrome.java @@ -0,0 +1,41 @@ +package com.bruis.algorithminjava.algorithm.leetcode.array; + +/** + * + * 125 + * + * 验证回文串 + * + * https://leetcode-cn.com/problems/valid-palindrome/ + * + * + * @author LuoHaiYang + */ +public class IsPalindrome { + public boolean isPalindrome(String str) { + int head = 0, tail = str.length() - 1; + char a, b; + while(head < tail) { + a = str.charAt(head); + b = str.charAt(tail); + if(!Character.isLetterOrDigit(a)) { + head ++; + } else if(!Character.isLetterOrDigit(b)) { + tail --; + } else { + if(Character.toLowerCase(a) != Character.toLowerCase(b)) { + return false; + } + head ++; + tail --; + } + } + return true; + } + + public static void main(String[] args) { + IsPalindrome isPalindrome = new IsPalindrome(); + String test = "race a car"; + System.out.println(isPalindrome.isPalindrome(test)); + } +} diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/array/MaximumGap.java b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/array/MaximumGap.java new file mode 100644 index 0000000..522d1f0 --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/array/MaximumGap.java @@ -0,0 +1,170 @@ +package com.bruis.algorithminjava.algorithm.leetcode.array; + +import java.util.Arrays; + +/** + * 最大间距 + * + * url:https://leetcode-cn.com/problems/maximum-gap/ + * + * @author LuoHaiYang + */ +public class MaximumGap { + + /** + * 基于桶排序 + * 时间复杂度:O(N) + * 空间复杂度:O(N) + * @param nums + * @return + */ + public int maximumGapOptimize2(int[] nums) { + if (nums.length < 2) return 0; + int len = nums.length; + + // 找出最大值和最小值 为了方便后面确定桶的数量 + int max = -1, min = Integer.MAX_VALUE; + for (int i = 0; i < len; i++) { + max = Math.max(nums[i], max); + min = Math.min(nums[i], min); + } + + // 排除nums全部为一样的数字,nums = [1,1,1,1,1,1]; + if (max - min == 0) return 0; + // 用于存放每个桶的最大值 + int[] bucketMin = new int[len - 1]; + // 用于存放每个桶的最小值 + int[] bucketMax = new int[len - 1]; + Arrays.fill(bucketMax, -1); + Arrays.fill(bucketMin, Integer.MAX_VALUE); + + // 确定桶的间距 + int interval = (int)Math.ceil((double)(max - min) / (len - 1)); + for (int i = 0; i < len; i++) { + // 找到每一个值所对应桶的索引 + int index = (nums[i] - min) / interval; + if (nums[i] == min || nums[i] == max) continue; + // 更新每个桶的数据 + bucketMax[index] = Math.max(bucketMax[index], nums[i]); + bucketMin[index] = Math.min(bucketMin[index], nums[i]); + } + + // maxGap 表示桶之间最大的差距 + int maxGap = 0; + // preMax 表示前一个桶的最大值 + int preMax = min; + for (int i = 0; i < len - 1; i++) { + // 表示某一个桶为空 + // 但凡某一个桶不为空,都会在前面的数据中更新掉bucketMax的值 + if (bucketMax[i] == -1) continue; + maxGap = Math.max(bucketMin[i] - preMax, maxGap); + preMax = bucketMax[i]; + } + // [1,10000000] + maxGap = Math.max(maxGap, max - preMax); + return maxGap; + } + + /** + * 基数排序: + * 时间复杂度:O(N) + * 空间复杂度:O(N) + * @param nums + * @return + */ + public int maximumGapOptimize(int[] nums) { + int n = nums.length; + if (n < 2) { + return 0; + } + long exp = 1; + int[] buf = new int[n]; + int maxVal = Arrays.stream(nums).max().getAsInt(); + + while (maxVal>= exp) { + int[] cnt = new int[10]; + for (int i = 0; i < n; i++) { + int digit = (nums[i] / (int) exp) % 10; + cnt[digit]++; + } + for (int i = 1; i < 10; i++) { + cnt[i] += cnt[i - 1]; + } + for (int i = n - 1; i>= 0; i--) { + int digit = (nums[i] / (int) exp) % 10; + buf[cnt[digit] - 1] = nums[i]; + cnt[digit]--; + } + System.arraycopy(buf, 0, nums, 0, n); + exp *= 10; + } + + int ret = 0; + for (int i = 1; i < n; i++) { + ret = Math.max(ret, nums[i] - nums[i - 1]); + } + return ret; + } + + public int maximumGap(int[] nums) { + if (nums == null || nums.length < 2) { + return 0; + } + // 排序 + quickSort(nums); + int n = nums.length; + + int max = nums[1] - nums[0]; + + for (int i = 2; i < n; i++) { + max = max(max, nums[i] - nums[i-1]); + } + return max; + } + + private void quickSort(int[] nums) { + int n = nums.length; + quickSort3ways(nums, 0, n-1); + } + + private void quickSort3ways(int[] nums, int left, int right) { + if (left>= right) { + return; + } + int p = nums[left]; + int i = left + 1, lt = left, gt = right + 1; + + while (i < gt) { + if (nums[i] < p) { + swap(nums, i, lt + 1); + i++; + lt++; + } else if (nums[i]> p) { + swap(nums, i, gt - 1); + gt--; + } else { + i++; + } + } + swap(nums, left, lt); + quickSort3ways(nums, left, lt - 1); + quickSort3ways(nums, gt, right); + } + + private int max(int i, int j) { + return Math.max(i, j); + } + + private void swap(int[] nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; + } + + public static void main(String[] args) { + int[] test = {3,6,9,1,20,15,11,30,31}; + MaximumGap maximumGap = new MaximumGap(); +// System.out.println(maximumGap.maximumGap(test)); + System.out.println(maximumGap.maximumGapOptimize2(test)); + } +} diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/array/ReverseVowels.java b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/array/ReverseVowels.java new file mode 100644 index 0000000..5dbb8aa --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/array/ReverseVowels.java @@ -0,0 +1,58 @@ +package com.bruis.algorithminjava.algorithm.leetcode.array; + +/** + * + * 345 + * + * https://leetcode-cn.com/problems/reverse-vowels-of-a-string/ + * + * 反转字符串中的元音字母 + * + * @author LuoHaiYang + */ +public class ReverseVowels { + + public String reverseVowels(String s) { + + char[] arr = s.toCharArray(); + int n = arr.length, left = 0, right = n - 1; + + while (left <= right) { + + // 如果不是元音,则指针右移 + while (left < n && !isVowel(arr[left])) { + left++; + } + + while (right>= 0 && !isVowel(arr[right])) { + right--; + } + + if (left> right) { + break; + } + + // 字符调换 + swap(arr, left, right); + left++; + right--; + } + return new String(arr); + } + + /** + * + * 1. 元音字母是? + * + */ + private boolean isVowel(char ch) { + return ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u' + || ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U'; + } + + private void swap(char[] arr, int i, int j) { + char tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } +} diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/array/TopKFrequentElements.java b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/array/TopKFrequentElements.java new file mode 100644 index 0000000..705e368 --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/array/TopKFrequentElements.java @@ -0,0 +1,69 @@ +package com.bruis.algorithminjava.algorithm.leetcode.array; + +import java.util.*; + +/** + * + * 前K个高频元素 + * + * url:https://leetcode-cn.com/problems/top-k-frequent-elements/ + * + * @author LuoHaiYang + */ +public class TopKFrequentElements { + + /** + * + * 桶排序 + * + */ + public int[] topKFrequent(int[] nums, int k) { + List res = new ArrayList(); + + if (nums == null || nums.length < 2) { + return nums; + } + Map count = new LinkedHashMap(); + int n = nums.length; + + for (int i = 0; i < n; i++) { + if (count.containsKey(nums[i])) { + count.put(nums[i], count.get(nums[i]) + 1); + } else { + count.put(nums[i], 1); + } + } + + List[] list = new List[nums.length]; + for (int key : count.keySet()) { + // 让频率作为下标 + int i = count.get(key); + if (list[i] == null) { + list[i] = new ArrayList(); + } + // key表示的是元素 + list[i].add(key); + } + + for (int i = list.length - 1; i>= 0 && res.size() < k; i--) { + if (list[i] == null) { + continue; + } + res.addAll(list[i]); + + } + int[] result = new int[res.size()]; + for (int i = 0; i < res.size(); i++) { + result[i] = res.get(i); + } + return result; + } + + public static void main(String[] args) { + TopKFrequentElements topKFrequentElements = new TopKFrequentElements(); + //int[] test = {1,1,1,2,2,3}; + int[] test = {3,0,1,0}; + topKFrequentElements.topKFrequent(test,1); + + } +} diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/array/TwoSumII.java b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/array/TwoSumII.java new file mode 100644 index 0000000..deb5cff --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/leetcode/array/TwoSumII.java @@ -0,0 +1,38 @@ +package com.bruis.algorithminjava.algorithm.leetcode.array; + +/** + * 167: + * + * https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/description/?utm_source=LCUS&utm_medium=ip_redirect_q_uns&utm_campaign=transfer2china + * + * 思路:指针碰撞 + * + * @author LuoHaiYang + */ +public class TwoSumII { + + public int[] twoSum(int[] numbers, int target) { + + if (numbers.length < 2) { + return numbers; + } + + int left = 0, right = numbers.length - 1; + + while (left <= right) { + + int result = numbers[left] + numbers[right]; + + if (result == target) { + int[] res = {left + 1, right + 1}; + return res; + } else if (result> target) { + right --; + } else { + left ++; + } + } + + return numbers; + } +} diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/sort/BinarySearch.java b/src/main/java/com/bruis/algorithminjava/algorithm/sort/BinarySearch.java new file mode 100644 index 0000000..b6de14f --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/sort/BinarySearch.java @@ -0,0 +1,31 @@ +package com.bruis.algorithminjava.algorithm.sort; + +/** + * + * 二分查找法 + * + * @author LuoHaiYang + */ +public class BinarySearch { + + public static int binarySearch(int[] arr, int n, int target) { + + // 在 [left, right]范围里寻找target + int left = 0, right = n - 1; + + while (left <= right) { + int mid = (right + left) / 2; + int nums = arr[mid]; + + if (nums == target) { + return mid; + } else if (nums> target) { + left = mid + 1; + } else { + // nums < target + right = mid - 1; + } + } + return -1; + } +} diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/sort/BucketSort.java b/src/main/java/com/bruis/algorithminjava/algorithm/sort/BucketSort.java new file mode 100644 index 0000000..08c7be4 --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/sort/BucketSort.java @@ -0,0 +1,57 @@ +package com.bruis.algorithminjava.algorithm.sort; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * 桶排序 + * + * @author LuoHaiYang + */ +public class BucketSort { + /* 排序原理: + + 桶排序本质就是空间换时间:时间复杂度为:O(n) + + 顺序从待排数组中取出数字,首先6被取出,然后把6入6号桶,这个过程类似这样:空桶[ 待排数组[ 0 ] ] = 待排数组[ 0 ] + [6 2 4 1 5 9] 待排数组 + [0 0 0 0 0 0 6 0 0 0] 空桶 + [0 1 2 3 4 5 6 7 8 9] 桶编号(实际不存在) + 顺序从待排数组中取出下一个数字,此时2被取出,将其放入2号桶,是几就放几号桶 + [6 2 4 1 5 9] 待排数组 + [0 0 2 0 0 0 6 0 0 0] 空桶 + [0 1 2 3 4 5 6 7 8 9] 桶编号(实际不存在) + 3,4,5,6省略,过程一样,全部入桶后变成下边这样 + [6 2 4 1 5 9] 待排数组 + [0 1 2 0 4 5 6 0 0 9] 空桶 + [0 1 2 3 4 5 6 7 8 9] 桶编号(实际不存在) + */ + private int range = 0; + public BucketSort(int range) { + this.range = range; + } + public int[] doSort(int[] arr) { + // 集合数组 + List[] aux = new LinkedList[range]; + for (int i = 0; i < aux.length; i++) { + aux[i] = new LinkedList(); + } + for (int i = 0; i < arr.length; i++) { + aux[arr[i]].add(arr[i]); + } + for (int i = 0, j = 0; i < aux.length && j < arr.length; i++) { + for (int v : aux[i]) { + arr[j] = v; + j++; + } + } + return arr; + } + + public static void main(String[] args) { + BucketSort bucketSort = new BucketSort(10); + int[] sort = bucketSort.doSort(new int[]{4, 1, 3, 2, 20, 6, 9, 9, 21, 19}); + System.out.println(Arrays.toString(sort)); + } +} diff --git a/src/main/java/com/bruis/algorithminjava/algorithm/sort/Heap.java b/src/main/java/com/bruis/algorithminjava/algorithm/sort/Heap.java new file mode 100644 index 0000000..f593c19 --- /dev/null +++ b/src/main/java/com/bruis/algorithminjava/algorithm/sort/Heap.java @@ -0,0 +1,122 @@ +package com.bruis.algorithminjava.algorithm.sort; + +/** + * 此堆索引从0开始 + * + * @Description + * @Author luohaiyang + * @Date 2022/4/20 + */ +public class Heap { + private int[] data; + private int count; + private int capacity; + + /** + * + * + * + * a + 1 + b c + 2 3 + d e f g + 4 5 6 7 + q w r x + 8 9 10 11 + * + * + * + * + */ + + /** + * 初始化堆 + * @param capacity + */ + public Heap(int capacity) { + this.capacity = capacity; + data = new int[capacity+1]; + count = 0; + } + + public Heap(int[] data, int capacity) { + this.data = data; + heapify(capacity); + } + + /** + * 新增一个元素 + * @param value + */ + public void insert(int value) { + if (count + 1> capacity) { + // 抛异常 + } + data[++count] = value; + shiftUp(count); + } + + /** + * 获取堆顶值 + * @return + */ + public int extractMax() { + if (count < 1) { + // 抛异常 + } + int max = data[1]; + swap(1, count--); + shiftDown(1); + return max; + } + + /** + * 堆化 + */ + public void heapify(int k) { + while (k/2>= 1) { + shiftDown(k/2); + k--; + } + } + + public int size() { + return count; + } + + public boolean isEmpty() { + return count == 0; + } + + /** + * 上浮操作 + * @param k + */ + private void shiftUp(int k) { + while (k> 1 && data[k]> data[k/2]) { + swap(k, k/2); + k /= 2; + } + } + + /** + * 下层操作 + * @param k + */ + private void shiftDown(int k) { + while (count>= k * 2) { + int j = k * 2; + if (j+1 <= count && data[j] < data[j+1]) j++; + if (data[k]>= data[j]) break; + swap(k, j); + k = j; + } + } + + private void swap(int a, int b) { + int tmp = data[a]; + data[a] = data[b]; + data[b] = tmp; + } +}

AltStyle によって変換されたページ (->オリジナル) /