1- ## 毕业总结
1+ ### 毕业总结
22
33为期十周的算法训练营结束了,感觉过的好快,但是收获还是挺大的,除了数据结构个和算法相关知识点的掌握外,特别是对** 刻意练习** 这个观点有了很强烈的认同,五毒神掌的思维不止能刷题,用到其他地方也是可以的。
44
55其实看看这十周学的东西还是挺多的。
66
77#### ** 数据结构:**
88
9- ###### 数组
9+ ##### 数组
1010
1111这是一种内存连续的线性表结构,每个存储单元占用的内存空间大小固定,且知道首地址,因此可以很快的用 首地址+空间大小 计算出任意元素的内存地址,** 查找的时间复杂度为O(1)** ,** 但是插入和删除的平均时间复杂度为O(n)** ,因为会涉及数据迁移,实际运用的时候需要注意,在进行大块内存申请的时候可能会失败,即使当前空间足够,因为数组需要的是连续空间,而现在内存里面大部分可能是分散的空间,所以这种时候比较推荐使用链表,对空间利用率会高点。
1212
13- ###### 链表
13+ ##### 链表
1414
1515是一种线性的存储结构,里面存储单元以节点的形式存在,节点里会包含指针(指向下一个节点,当指针指向null则代表到了尾部)和存储的数据。
1616
1717** 链表查找是O(n),因为需要用指针从头开始遍历,插入删除则是O(1)** 。在实际中会做很多优化,如双向链表,以及和map一起使用等。链表相比数组是每个节点的内存空间随机,且数据非顺序,这样存储会十分灵活。
1818
19- ###### 栈
19+ ##### 栈
2020
2121先入后出的数据结构,一般用来做括号匹配,以及面积判断,接雨水等问题求救。
2222
23- ###### 队列
23+ ##### 队列
2424
2525队列是一种先入先出的数据结构,在实际业务场景中运用的还是比较多的,如消息中间件等,队列里有一个特殊存在,双端队列,可以两头操作,基本一个数据结构就囊括了栈和队列所有特性。
2626
27- ###### hashMap
27+ ##### hashMap
2828
2929** 哈希表** 是一种在数组上扩展出来的数据结构,它实际还是用数组存储的数据,只是做了一定封装。哈希表一个特点是可以根据输入O(1)复杂度找到对应的数据,其实就是利用数组查询是O(1)的特性以及hash函数对输入数据进行转换为数组下标index的过程。
3030
3434
3535当元素个数<=8时,用链表,当元素个数>8时,则用红黑树。
3636
37- ###### Set
37+ ##### Set
3838
3939set是一种无序且不重复的数据结构,长用于去重和缩小数据量的操作,java里面的实现比较有意思,就是直接用map的key当做Set的value,然后map的value直接用一个固定值站位,因为不会用到它。
4040
41- ###### 跳表
41+ ##### 跳表
4242
4343跳表(Skip-list)只能用于元素有序的情况,你可以想象成链表的升级版,它在链表上加了索引,从而提升了查询速度,在现在的开源项目中一般都用跳表代替红黑树,易实现好维护。
4444
45- ###### 树
45+ ##### 树
4646
4747树是一种十分常用的数据结构,其实当一个链表的节点由一个变成两个或多个就变成树,树的实现有很多,二叉搜索树,avl树,红黑树,B+树等。
4848
4949因为二叉树分为左右两个子树,且左右子树和二叉树特性相同,因此常常用递归去实现树的操作。
5050
51- ###### 二叉搜索树
51+ ##### 二叉搜索树
5252
5353空树也是二叉搜索树,二叉搜索树的左儿子小于根节点,且根节点小于右儿子,左右子树同理可得。
5454
5555二叉搜索树在查找的时候时间复杂度为O(logn),它的插入和查找类似,先进行查找,如果查找的位置没有节点,则在那个位置插入。
5656
5757删除的话会麻烦点,当删除的节点是叶子节点,直接删除就行,当删除的节点是父亲节点,就需要将该父亲节点右子树里面最小的节点放到该父亲节点。
5858
59- ###### 堆
59+ ##### 堆
6060
6161堆常用来求topk的问题,堆分为大顶堆和小顶堆,以大顶堆为例,堆里元素子节点都小于父亲节点,左右子树同理可得。
6262
@@ -66,11 +66,11 @@ set是一种无序且不重复的数据结构,长用于去重和缩小数据
6666
6767堆的删除会比较麻烦,它会将堆顶元素取走,然后将堆尾的元素放到堆顶,然后重新构建整个堆。时间复杂度也是O(logn)
6868
69- ###### avl树
69+ ##### avl树
7070
7171严格的平衡二叉树,需要记住四种旋转平衡,但是就是由于太严格了,有了很多不必要的操作,所以后面相处了红黑树。
7272
73- ###### 红黑树
73+ ##### 红黑树
7474
7575红黑树是一个近似平衡的二叉树,它不需要频繁的去触发平衡操作,这样会省去很多不必要的操作。它能确保任何一个几点左右子树的高度差小于两倍。
7676
@@ -103,38 +103,167 @@ recur(level+1,max,....)
103103
104104分治是采用分而治之的思想,将一个大问题拆成几个小问题,然后分别将小问题求解之后,在组装其结果,最后得出最终结果。计算机天生适合处理分治递归等问题,因为它们能并行。
105105
106- ###### DFS,BFS,双向BFS,启发式搜索(A* )
106+ ##### DFS,BFS,双向BFS,启发式搜索(A* )
107107
108108上面这些都是搜索算法,在一个图或者树中对目标结果进行搜索,根据方式不同分为
109109
110- 深度优先搜索是指将每条路径直接走到底,当该条路径遍历完都没有找到目标结果时,在换另一条,直到找到。有种不撞南墙不回头的意思。
110+ 深度优先搜索是指将每条路径直接走到底,当该条路径遍历完都没有找到目标结果时,在换另一条,直到找到。有种不撞南墙不回头的意思。其实现一般是递归,当然栈也可以,但是多用递归。
111111
112- 广度优先搜索是指按照波纹扩散的方式将对每条路的下一层节点进行遍历,直到找到目标节点。我感觉实际中推荐用BFS,因为DFS当数据量太大时很可能带来较高的时间复杂度。
112+ 广度优先搜索是指按照波纹扩散的方式将对每条路的下一层节点进行遍历,直到找到目标节点。我感觉实际中推荐用BFS,因为DFS当数据量太大时很可能带来较高的时间复杂度。实现一般是队列。
113113
114- 双向BFS指的是在BFS的基础上进行优化,常规BFS是从起点到终点扩散,而双向BFS是从起点和终点同时向中间扩散,且每次优先用节点少的那端扩散,当起点和终点直接深度很深的时候,双向BFS能比常规BFS快很多。
114+ 双向BFS指的是在BFS的基础上进行优化,常规BFS是从起点到终点扩散,而双向BFS是从起点和终点同时向中间扩散,且每次优先用节点少的那端扩散,当起点和终点直接深度很深的时候,双向BFS能比常规BFS快很多。双向BFS在代码实现的时候就不是用queue而是用两个set去遍历了。
115115
116116启发式搜索(A* ),这个定义就比较灵活了,你可以自己制定比较规则,然后按照你制定的比较规则进行遍历。
117117
118- ###### 贪心算法
118+ ##### 贪心算法
119119
120+ 在计算的每个阶段都得到最优解,从而得到全局的最优解,它可以说是动态规划的一种特殊情况,即每个阶段的解刚好就是它自己,没有额外的回退或者转换。** 贪心算法** 比较麻烦的是你能证明或者想到这个题能用贪心解,当你知道能用贪心解,其代码实现一般是比较简单的,主要是判定和证明比较麻烦。
120121
122+ ##### 二分查找
121123
124+ 二分查找试用数据集有三个特性:
122125
126+ 1 . 单调性
127+ 2 . 有上下边界
128+ 3 . 能通过下标快速访问
123129
130+ 常规模板如下:
124131
132+ ``` java
133+ public int binarySearch(int [] nums,int target){
134+ int left = 0 , right = nums. length - 1 ;
135+
136+ while (left <= right) {
137+
138+ int mid = left + (right - left) / 2 ;
139+
140+ if (nums[mid]== target) {
141+ return mid;
142+ } else if (nums[mid] > target) {
143+ right = mid - 1 ;
144+ } else {
145+ left = mid + 1 ;
146+ }
147+ }
148+ }
149+ ```
150+
151+ ##### 动态规划
152+
153+ 动态规划是算法里面的大头,也是难点,我理解所有递归的问题都可以用动态规划推到出来解法。
154+
155+ 它有两种解题方式,** 自顶向下** ,先由递归思路往下走,然后在加入记忆化缓存,然后反着推出状态转移方程,这种方法初学者前期用的比较多,便于理解,缺点是会比较慢,因为需要先推一次递归。** 自下而上** ,直接从下而上,根据有限的初始值得到最终想要的结果,这个需要对动态转移方程定义有熟练的掌握才行,需要多做题去掌握和熟练这种方式。以斐波那契数列为例,我们可以由f(0)和f(1)一直向上递推,得到f(n)。
156+
157+ ##### 位运算
158+
159+ 计算机底层的计算,直接在二进制上面进行运算,计算速度会比高级语言的计算快,一个有一定代码功底的开发基本都要会的东西。
160+
161+ ##### 布隆过滤器
162+
163+ 布隆过滤器算是对哈希表的一种泛化数据结构,当在某些业务场景下,我们只需要知道这个东西在集合里面是否存在,而不需要知道这个东西的具体值时,布隆过滤器就派上用场了。
164+
165+ 布隆过滤器的实现原理是利用一系列随机哈希函数将元素存在一个很长的二进制位将元素里,一个元素会占几个bit位,但是当可能存在一个bit位被多个元素占用的时候,这个时候就会发生误判。
166+
167+ 因此布隆过滤器的优缺点也就出来了,** 优点** 就是空间效率和查询时间都远远高于一般的算法,** 缺点** 就是会发生误判和删除困难。
168+
169+ ** 布隆过滤器里面元素如果不存在一定是不存在的,但是存在是可能存在的,你还需要进一步验证是否存在。**
170+
171+ 因此布隆过滤器一般被用作最外层的过滤网,删除一些没用的信息,比如垃圾邮件判定等。
172+
173+ ##### LRU Cache
174+
175+ 是一个使用比较多的缓存替换算法,最近最长使用缓存,它比较符合人脑的思维,如果一个物品,你如果很长时间没有使用的话,算法就会将其舍弃,且一个物品你前段时间没用最近开始使用了,算法会将这个物品放到最前面。它的实现一般基于HashMap+LinkedList(双向链表)实现,java里面有现成的实现->linkedHashMap。
176+
177+ ##### 字典树
178+
179+ 字典树在搜索的时候经常会用到,可以用O(1)的复杂度找到查找的词组且补全高频查找结果,一般搜索引擎查询补全用的比较多。
180+
181+ 字典树也是一种树,只是这个树和我们前面学的BFS和二叉堆不太一样,这个是颗N叉树,再者数据存储也不太一样,正常树的节点是存储完整的数据,而字典树每个节点其实只会存一个字符,当其到达尾部之后(一般条件是isEnd为true),将整个遍历路径连起来会组成一个字符串,这就是查询的结果。
182+
183+ 字典树的缺点是空间占用会很大,是一种典型的空间换时间的思想。
184+
185+ ##### 并查集
186+
187+ 并查集这块东西会比较死,记住模板然后认清使用场景,直接套上去用就行了,基本是定式操作,主要是牢记模板。
188+
189+ 其使用场景主要是组团和配对问题。判断两个人是否是朋友,判断群组关系等。
190+
191+ PS:并查集现在做的题比较少,还没有产生概念,需要加强。
192+
193+ ##### 排序
194+
195+ 排序算法有很多,初级的如冒泡排序,选择排序,插入排序等,都是O(n^2)的时间复杂度,还有线性排序有计数排序,桶排序,基数排序等都是时间复杂度O(n)的算法,但是试用场景很局限,都是必须是常数Integer类型的排序,对于字符串这种完全不适用。
196+
197+ 高级排序主要有快速排序和归并排序,这两个都是用的分治的思想,只是思考方式有点不一样。其时间复杂度是O(nlogn),模板如下:
198+
199+ 快速排序:
200+
201+ ``` java
202+ public static void quickSort(int [] array, int begin, int end) {
203+ if (end <= begin) return ;
204+ int pivot = partition(array, begin, end);
205+ quickSort(array, begin, pivot - 1 );
206+ quickSort(array, pivot + 1 , end);
207+ }
208+
209+ static int partition(int [] a, int begin, int end) {
210+ // pivot: 标杆位置,counter: 小于pivot的元素的个数
211+ int pivot = end, counter = begin;
212+ for (int i = begin; i < end; i++ ) {
213+ if (a[i] < a[pivot]) {
214+ int temp = a[counter];
215+ a[counter] = a[i]; a[i] = temp;
216+ counter++ ;
217+ }
218+ }
219+ int temp = a[pivot];
220+ a[pivot] = a[counter];
221+ a[counter] = temp;
222+ return counter;
223+ }
224+ ```
225+
226+ 归并排序:
227+
228+
229+
230+ ``` java
231+ public static void mergeSort(int [] array, int left, int right) {
232+ if (right <= left) return ;
233+ int mid = (left + right) >> 1 ; // (left + right) / 2
234+ mergeSort(array, left, mid);
235+ mergeSort(array, mid + 1 , right);
236+ merge(array, left, mid, right);
237+ }
238+
239+ public static void merge(int [] arr, int left, int mid, int right) {
240+ int [] temp = new int [right - left + 1 ]; // 中间数组
241+ int i = left, j = mid + 1 , k = 0 ;
242+ while (i <= mid && j <= right) {
243+ temp[k++ ] = arr[i] <= arr[j] ? arr[i++ ] : arr[j++ ];
244+ }
245+
246+ while (i <= mid) temp[k++ ] = arr[i++ ];
247+ while (j <= right) temp[k++ ] = arr[j++ ];
248+
249+ for (int p = 0 ; p < temp. length; p++ ) {
250+ arr[left + p] = temp[p];
251+ }
252+ // 也可以用 System.arraycopy(a, start1, b, start2, length) }
253+ ```
254+
255+ 当然还有利用前面堆这种数据结构的堆排序,时间复杂度是O(nlogn)。每个元素建堆是logn,n个元素就是nlogn。
256+
257+ ##### 字符串比较算法
258+
259+ 字符串的基本操作在实际开发中还是比较常用的,但是初级字符串比较大多数会比较简单,而高级的字符串比较算法现在掌握的还不够,题基本没来得及练,至于kmp算法,基本是懵逼的。先用时间消化吧。
260+
261+ ** 以上就是算法训练营所有内容,虽然看起来挺少,但是知识点细分和理解掌握还是会话很多时间的。上面其实有很多知识点当时学的时候由于时间原因没有掌握的那么透彻,需要花时间去掌握的。**
262+
263+ 都毕业了,对于我来说感觉还是任重而道远,这个状态去面试可能还有问题,应该会拿2 - 3 个月时间准备下。整体来看我有的知识点掌握的还行,如基本的数据结构,都知道使用场景和实现了,在了解其实现后在实际开发中确实会用的更加得心应手了。但有的还是不够熟练,需要过遍数,如并查集,位运算,字符串算法KMP 算法等,现在基本还是半懵逼状态,需要多做题去消化。比较庆幸的一点是,对于** 递归** 和** 动态规划** 还是有自己的理解,其核心思想都大概掌握了,接下来就是多做题强化了。
125264
126- - 递归,回溯,分治,
127- - BFS, DFS,双向BFS,启发式搜索(A* )
128- - 贪心算法
129- - 二分查找
130- - 排序
131- - 动态规划
132- - 位运算
133- - 布隆过滤器
134- - LRU Cache
135- - 字典树和并查集
136- - 字符串比较算法
265+ ** 就像超哥说的那样,“师傅领进门,修行在个人。”他只能帮我们提点一下,后面还是要靠自己。你的努力程度,决定你能走多远。**
137266
138- 其中有的知识点掌握的还行,如基本的数据结构,都知道使用场景和实现了,在了解其实现后在实际开发中确实会用的更加得心应手了。但有的还是不够熟练,需要过遍数,如并查集,位运算,字符串算法KMP算法等,现在基本还是半懵逼状态,需要多做题去消化。比较庆幸的一点是,对于 ** 递归 ** 和 ** 动态规划 ** 还是有自己的理解,其核心思想都大概掌握了,接下来就是多做题强化了。
267+ 算法训练营结束了,但是对我来说 ** 毕业也只是开始,多刷题多学习,争取能拿到自己心仪公司的offer,加油! **
139268
140269最后感谢算法训练营所有老师和助教以及班班的帮助!
0 commit comments