算法笔记-排序算法
排序算法
nlogn排序: 堆排序、快速排序、归并排序
初级排序
O(n^2)
- 选择排序(Select Sort)每次找最小值,然后放到待排序数组的起始位置。
- 插入排序(Insert Sort)从前到后逐步构建有序序列;对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
- 冒泡排序(Bubble Sort)嵌套循环,每次查看相邻的元素如果逆序,则交换。
高级排序
O(n*logn)
- 快速排序(Quick Sort)数组取标杆pivot,将小元素放pivot左边,大元素放右边,然后依次对右边和左边的子数组继续快排;以达到整个序列有序
- 归并排序(Merge Sort)
1.把长度为n的输入序列分成两个长度位n/2的子序列;
2.把这两个子序列分别采用归并排序;
3.将两个排序好的子序列合并成一个最终的排序序列;
归并排序和快速排序具有相似性,但步骤相反
归并:先排序左右子数组,然后合并两个有序子数组
快排:先调出左右子数组,然后对于左右数组进行排序
- 对排序(heap Sort) 堆插入O(logN),取最大最小值O(1)
1.数组元素依次建立小堆顶
2.依次取堆顶元素,并删除
(满二叉搜索树进行实现)
拓扑排序
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
1 |
|
相关题目
TopK问题(中等)
top-k问题 主要解法:排序、堆、随机快速选择、BFPRT 排序方法:时间复杂度O(NlogN), 因为本题数据特殊性 可以使用计数排序 时间复杂度O(N)
堆:维护一个大小为k的堆, 不太建议使用priority_queue, 额外空间和数据拷贝,可以原地操作数组,使用make_heap + pop_heap、push_heap 或 覆盖堆顶,自实现堆顶下沉 时间复杂度O(Nlogk)
随机快速选择:平均时间复杂度 O(N)
BFPRT:最坏时间复杂度 O(N)
1 |
|
快排解题思路
我们知道,经过快速排序算法中的一次划分后,基点左边的所有数小于基点,右边的所有数大于基点,基点位置pivot有三种情况:
- pivot == k 说明基点就是第k+1个小的元素,其左边的子数组就是最小的k个数。此时的子数组[0, k) 就是答案
- pivot > k 说明基点”偏大”了,对其左子数组继续进行划分
- pivot < k 说明基点”偏小”了,对其右子数组继续进行划分
快排代码实现
1 |
|
BFPRT算法思路(O(n))
BFPRT算法又称中位数的中位数算法,可以用于在O(n)的时间复杂度内找到第k小的数。对于本题,我们可以用BFPRT算法找到第k小的数O(n),然后遍历一遍原数组O(n),将小于第k小的数的元素加入到答案数组,即得正确解(时间复杂度也是O(n))。
BFPRT算法的主要步骤如下:
- 将 [公式] 个元素划为 [公式] 组,每组5个,至多只有一组由 [公式] 个元素组成。
- 寻找这 [公式] 个组中每一个组的中位数,这个过程可以用插入排序。
- 对步骤2中的 [公式] 个中位数,重复步骤1和步骤2,递归下去,直到剩下一个数字。
- 最终剩下的数字即为pivot,把大于它的数全放左边,小于等于它的数全放右边。
- 判断pivot的位置与k的大小,有选择的对左边或右边递归。
代码实现
1 |
|
最小的k个数
1 |
|
剑指 Offer 51. 数组中的逆序对(hard)
在归并的过程中将原始数组一分为二,分别对其进行排序,排序完成后进行合并,每次将两个有序数组中的较小的数放入新数组中,在这合并的过程中每当我们选取右边数组的数加入新数组时,能与它组成的逆序数就是左边数组剩余的个数(因为在原始数组中右边数组每个数都在左边数组的右边且当前左边剩余的数都比当前要插入新数组的数大),当选取的是左边的数时无需再重复计数(已经递归计算过)
1 |
|
子集(中等)
给你一个整数数组 nums ,返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。
1 |
|
字典排序(二进制排序)
nums=[5,2,9]
0/1 序列 | 子集 0/1 | 0/1 序列对应的二进制数 |
---|---|---|
000 | {} | 0 |
001 | {9} | 1 |
010 | {2} | 2 |
011 | {2,9} | 3 |
100 | {5} | 4 |
101 | {5,9} | 5 |
110 | {5,2} | 6 |
111 | {5,2,9} | 7 |
1 |
|
递归回溯,深度优先遍历
1 |
|
qi
前K个高频元素
本题和TopK有些类似,题目要优于NlogN,可以采用堆这种结构进行存储,设置堆大小为k,当堆存储个数小于k直接插入,否则比较新的元素和堆顶元素频率,留下频率高的
1 |
|
基于快排,通过map进行统计每个元素的出现次数,然后通过对元素出现的次数(次数次数次数重要的事情说三遍)进行排序即可取出前k个得出答案。
1 |
|
349. 两个数组的交集(easy)
1 |
|
350. 两个数组的交集 II(easy)
1 |
|
252. 会议室(easy)
1 |
|
253. 会议室 II(mid)
1 |
|
242. 有效的字母异位词(easy)
1 |
|
剑指 Offer 45. 把数组排成最小的数(mid)
1 |
|
280. 摆动排序(mid)
1 |
|
324. 摆动排序 II(mid)
1 |
|
179. 最大数
1 |
|
148. 排序链表(mid)
1 |
|
56. 合并区间(mid)
1 |
|
767. 重构字符串(mid)
1 |
|
1353. 最多可以参加的会议数目(mid)
1 |
|
922. 按奇偶排序数组 II(easy)
1 |
|
976. 三角形的最大周长(easy)
1 |
|
1356. 根据数字二进制下 1 的数目排序(easy)
1 |
|
973. 最接近原点的 K 个点(mid)
1 |
|
1288. 删除被覆盖区间(mid)
1 |
|
164. 最大间距(hard)
1 |
|
315. 计算右侧小于当前元素的个数(hard)
本题和 剑指 Offer 51. 数组中的逆序对 解题思路类似,采用归并排序,在归并的过程中计算
原始数组 = [8,16,12,100,22] [55,64,7,26,91]
归并排序 L = [8,12,16,22,100] R = [7,26,55,64,91] M = [] (排序后两个指针指向两个数组开始)
l r
第一轮 L = [8,12,16,22,100] R = [7,26,55,64,91] M = [7] 此时(7<8)所以把7加入M,r向右移一位
l r
第二轮 L = [8,12,16,22,100] R = [7,26,55,64,91] M = [7,8] 此时(8<26)所以把8加入M,同时新数组中已有1个数且必定小于8,新数组中包含L数组的元素
l r 个数是0,所以元素8右侧比8小的元素=M.length-0=1-0=1,l向右移一位
第三轮 L = [8,12,16,22,100] R = [7,26,55,64,91] M = [7,8,12] 此时(12<26)所以把12加入M,同时新数组中已有2个数且必定小于12,新数组中包含L数组的元素
l r 个数是1,所以元素12右侧比12小的元素=M.length-1=2-1=1,l向右移一位
第四轮 L = [8,12,16,22,100] R = [7,26,55,64,91] M = [7,8,12,16] 此时(16<26)所以把16加入M,同时新数组中已有3个数且必定小于16,新数组中包含L数组的元素
l r 个数是1,所以元素16右侧比16小的元素=M.length-1=3-2=1,l向右移一位
………..
在编码的时候我们需要注意的是记录元素在原数组中的下标位置所以需要建立一个数组来存储。
1 |
|