算法模式分类总结
最近工作之余,开始想要温习各类算法,给自己定个计划,对算法来个“朝花夕拾”。在知乎看到一个非常好的刷题类型总结,这里也做一个记录总结分类。
1. Pattern: Sliding window(滑动窗口类型)
滑动窗口类型的算法题经常是用来执行数组或者链表上某个区间(窗口)上的操作。
经典算法题如下:
Max Sum Subarray of Size KSmallest Subarray with a given sumLongest SubString with K Distinct CharactersFruits into BasketsNo-repeat SubstringLongest Substring with Same Letters after ReplacementLongest Subarray with Ones after Replacement
2. Pattern: Two Pointers(双指针类型)
双指针模式:两个指针朝着左右方向移动(同向双指针、异向双指针),直到指针有一个或两个同时满足某种条件。
经典算法题如下:
Pair with Target SumRemove DuplicatesSquaring a Sorted ArrayTriplet Sum to ZeroTriplet Sum Close to TargetTriplets with Smaller SumSubarrays with Product Less than a TargetDutch National Flag Problem
3. Pattern: Fast & Slow pointers(快慢指针类型)
龟兔赛跑,这种算法的两个指针在数组上(或是链表上、序列上)的移动速度不一样。这种方法在解决有环的链表和数组时特别有用,快的指针肯定会追上慢的指针。
适用于以下场景:
- 问题需要处理环上的问题,比如环形链表和环形数组;
- 当你需要知道链表的长度或是某个特别位置信息的时候;
- 单链表无法往回移动,建议用双指针。
经典算法题如下:
LinkedList CycleStart of LinkedList CycleHappy NumberMiddle of the LinkedList
4. Pattern: Merge Intervals(区间合并类型)
用来处理有区间重叠的问题很高效。
经典算法题如下:
Merge IntervalInsert IntervalIntervals IntersectionConflicting Appointments
5. Pattern: Cyclic Sort(循环排序)
用来处理数组中的数值限定在一定区间的问题。一般设计在排序好的数组,而且数值一般满足于一定的区间。比如要你在排序好的数组中,寻找丢失/重复/最小的元素。
经典算法题如下:
Cyclic SortFind the Missing NumberFind all Missing NumbersFind the Duplicate NumberFind all Duplicate Numvers
6. Pattern: In-place Reversal of a LinkedList (链表翻转)
一般题目要求你翻转某一段的节点,有时候要求你全部翻转。用来处理原地翻转,而不需要使用额外的空间。
经典算法题如下:
Reverse a LinkedListReverse a Sub-listReverse every K-element Sub-list
7. Pattern: Tree Breadth First Search (树上的BFS)
一般题目用于宽搜,适用于需要遍历一棵树。可能需要用到每一层的节点做运算,可借助于队列数据结构,保证将树的节点按照层数打印出来。其模式通过把根节点加到队列中,然后不断的遍历直到队列为空。每次循环中,将队头节点remove掉,再对其做相关操作。在删除每个节点的同时,其孩子节点会被加入到队列中。【层序遍历】
经典算法题如下:
Binary Tree Level Order TravelsalReverse Level Order TraversalZigzag TraversalLevel Averages in a Binary TreeMinimum Depth of a Binary TreeLevel Order SuccessorConnect Level Order Siblings
8. Pattern: Tree Depth First Search (树上的DFS)
树形DFS基于深搜来实现树的遍历。可用递归、栈的方式来记录遍历过程中访问过的父节点,该模式的运行方式是从根节点开始,如果该节点不是叶子节点:
①需要区别是使用前序、中序、后序来处理根节点。
②递归处理当前节点的左右孩子。
该问题的解一般离叶子节点比较近。
经典算法题如下:
Binary Tree Path SumAll Paths for a SumSum of Path NumbersPath With Given SquenceCount Paths for a Sum
9. Pattern: Two Heaps (双堆类型)
一般题目给你一大堆可以分成两队的数据,例如让你把小的数字都放在一起,大的放在另外一队。最小堆用来找最小元素;最大堆拿到最大元素。该模式下将一半的元素放在最大堆中,这样你可以在该堆中迅速找到最大元素;再将剩下的一半丢到最小堆中,又能迅速找到最小元素,且查询最大、最小的复杂度都在O(1)。通过这样的方式,这一堆元素的中位数就可以从两个堆的堆顶拿到数字,就方便计算了。
1、计划安排问题中有奇效。
2、从一组数中寻找最大/最小/中位
3、在二叉树结构里也很有用。
经典算法题如下:
Find the Median of a Number StreamSliding Window MedianMaximize Capital
10. Pattern: Subsets (子集类型)
一般用于多重DFS,很多题目会涉及到排列和组合的问题。子集问题是用BFS来处理这些问题。
给出一组数字集合[1,5.3]
1、首先是初始化空集:[[]];
2、把第一个数1,加到之前已经存在的集合中[[],[1]];
3、把第二个数5,加到之前的集合中得到[[],[1],[5],[1,5]];
4、最后加上第三个数3,则为[[],[1],[5],[1,5],[3],[1,3],[5,3],[1,5,3]].
经典算法题如下:
SubsetsSubsets With DuplicatesPermutationsString Permutations by changing caseBalanced ParenthesesUnique Generalized Abbreviations
11. Pattern: Modified Binary Search (改造过的二分搜索)
当你需要解决的问题输入是排序好的数组、链表或者是排序好的矩阵,要求寻找某些特定元素。这个时候最好的选择就是二分搜索。
对于一组满足上升排序的数集来说,以下步骤是这样的:
1、首先算出左右端口的中点。最简单的方式是:middle= (start + end)/2。但这种计算方式有概率会出现整数越界。因此一般使用这种写法:middle=start + (end - start)/2
2、如果要找的目标刚好和中点所在的数值相等,我们返回中点的下标即可。
3、如果目标不相等:我们有两种移动方式:
-
如果目标比中点值小,将下一步搜索空间放在中点位置的左边(end=middle - 1)
-
如果目标比中点值大,将下一步搜索空间放在中点位置的右边(start=middle + 1)
经典算法题如下:
Order-agnostic Binary SearchCeiling of a NumberNext LetterNumber RangeSearch in a Sorted Infinite ArrayMinimum Difference ElementBitonic Array Maximum
12. Pattern: Top 'K' Elements (前K个系列)
任何让求解最大、最小、最频繁的K个元素的题,都遵循该模式。
用来记录这种前K类型的最佳数据结构就是堆。这种模式借助堆来解决很多这种前K个数值的问题。该模式如下:
-
根据题目要求,将K个元素插入到最大堆或最小堆。
-
遍历剩下还没有访问的元素,如果当前元素比堆顶元素大,就先把堆顶元素删除,再加当前元素进去。
该模式下无需对数组做排序,堆具有良好的局部有序性,对于解决问题已足够。
经典算法题如下:
Top 'K' NumbersKth Smallest Number'K' Closest Points to the OriginConnect RopesTop 'K' Frequent NumbersFrequency SortKth Largest Number in a Stream'K' Closest NumbersMaximum Distinct ElementsSum of ElementsRearrange String
13. Pattern: K-way merge (多路归并)
K路归并能解决涉及多组已经排序好的数组问题。
每当输入为K个排好序的数组,你就可以用堆来高效顺序遍历其中所有数组的元素。可以将每个数组中的最小元素加入到最小堆,从而得到全局最小值。当拿到全局最小值后,再从该元素所在数组里取出其后紧挨着的元素加入堆,如此往复直到处理完所有元素。
该模式是这样运行的:
- 把每个数组中的第一个元素加入最小堆。
- 取出堆顶元素,将该元素放入排好序的结果结合中。
- 将刚取出的元素所在的数组里面的下一个元素加入堆。
- 重复上述两个步骤,直到处理完所有数字。
识别K路归并:
-
该问题的输入是排好序的数组、链表或矩阵。
-
如果需要合并多个排好序的结合,或是找到这些集合中最小的元素。
经典算法题如下:
Merge K Sorted ListsKth Smallest Number in M Sorted ListsKth Smallest Number in a Sorted MatrixSmallest Number Range
14. Pattern: 0/1 Knapsack (动态规划-0/1背包问题)
经典算法题如下:
0/1 KnapsackEqual Subset Sum PartitionSubset SumMinimum Subset Sum Difference
15. Pattern: Topological Sort(Graph) (拓扑排序)
拓扑排序用来寻找一种线性的排序,这些元素之间具有依赖性。例如,事件B依赖于事件A,那A在拓扑排序中排在B前面。
该模式是这样奏效的:
- 初始化
①借助于HashMap将图保存成邻接表形式。
②找到所有的起点,用HashMap来帮助记录每个节点的入度。 - 创建图,找到每个节点的入度。
①利用输入把图建好,然后遍历一下图,将入度信息记录在HashMap中。 - 找所有的起点
①所有入度为0的节点,都是有效的起点,都将其加入到一个队列中。 - 排序
①对每个起点,执行以下步骤:
(1) 把它加到结果的数组中
(2) 将其在图中的孩子节点取到。
(3) 将其孩子节点的入度减1
(4) 如果孩子节点的入度为0,则将孩子节点改为起点,将其加入队列中。
②重复①中步骤,直到起点队列为空
拓扑排序模式识别
-
待解决的问题需要处理无环图
-
需要以一种有序的秩序更新输入元素
-
需要处理的输入遵循某种特定的顺序
经典算法题如下:
Topological SortTasks SchedulingTasks Scheduling OrderAll Tasks Scheduling OrdersAlien Dictionary
16. Pattern: Topological Sort(Graph) (拓扑排序)
拓扑排序用来寻找一种线性的排序,这些元素之间具有依赖性。例如,事件B依赖于事件A,那A在拓扑排序中排在B前面。
该模式是这样奏效的:
- 初始化
①借助于HashMap将图保存成邻接表形式。
②找到所有的起点,用HashMap来帮助记录每个节点的入度。 - 创建图,找到每个节点的入度。
①利用输入把图建好,然后遍历一下图,将入度信息记录在HashMap中。 - 找所有的起点
①所有入度为0的节点,都是有效的起点,都将其加入到一个队列中。 - 排序
①对每个起点,执行以下步骤:
(1) 把它加到结果的数组中
(2) 将其在图中的孩子节点取到。
(3) 将其孩子节点的入度减1
(4) 如果孩子节点的入度为0,则将孩子节点改为起点,将其加入队列中。
②重复①中步骤,直到起点队列为空
拓扑排序模式识别
-
待解决的问题需要处理无环图
-
需要以一种有序的秩序更新输入元素
-
需要处理的输入遵循某种特定的顺序
经典算法题如下:
Topological SortTasks SchedulingTasks Scheduling OrderAll Tasks Scheduling OrdersAlien Dictionary
文章内容总结来自:www.zhihu.com/question/36…
仅做个人总结使用,如需商用请咨询作者本人。