1.插入排序
1.1 思想
保障数组左边数字都有序,每次从右边开始往左边有序数组里插入数据保证其有序。
1.2 示意图
1.3 伪代码
INSERTION_SORT(A) {
for(j = 2 to A.length) {
key = A[j];
i = j - 1;
while(i >= 1 and A[i] > A[j]) {
A[i + 1] = A[j];
i = i - 1;
}
A[i + 1] = key;
}
}
1.4 分析
- 时间复杂度
最好的情况下: 最外层循环一次 O(n)
最坏的情况下: O(n2) - 空间复杂度
O(1)
2.归并排序
2.1 思想
将原数组分为两个数组,每次都取最小的一个值放入新数组中
2.2 方法
2.3 伪代码
//合并操作
//假设数组A[p] - A[q] 和 A[q+1] - A[r] 两段数组都已排好序,然后将他们合并
MARGE_SORT(A, p, r) {
if (p < r) {
q = (p + r) / 2;
MARGE_SORT(A, p ,q);
MARGE_SORT(A, q + 1, r);
MERGE(A, p, q, r);
}
}
MERGE(A, p, q, r) {
n1 = q - p + 1;//第一个数组的长度
n2 = r - q;//第二个数组的长度
new L[1...n1]; //声明两个新数组 L,R
new R[1...n2];
L[n1] = oo; //哨兵
R[n2] = oo;
i = j = 0;
for(for k = p to r){
if(L[i] < R[j]) {
A[k] = L[i];
i++;
} else {
A[k] = R[j];
j++;
}
}
}
2.4 分析
- 时间复杂度
O(n*lgn) - 空间复杂度
O(n)
3.堆排序
3.1 (二叉)堆
- 堆是一种完全二叉树
- A[PARENT(i)] >= A[i] (最大堆)
3.2 父节点和子节点
求父节点
PARENT(i) { retrun [i/2]; }
求左节点
LEFT(i) { return i * 2; }
求右节点
RIGHT(i) { RETURN i * 2 + 1; }
3.3 维护堆的性质
3.3.1 思想
我们假定A[i]节点的左子树和右子树都是最大堆,但这时A[i]有可能小于其孩子。那MAX-HEAPIFY[i]通过让A[i]的值在最大堆中逐级下降,从而遵守最大堆的性质。
3.3.2 示意图
3.3.3伪代码
//维护最大堆
MAX-HEAPIFY(A, i) {
l = LEFT[i];
r = RIGHT[i];
if(l <= A.heap-size and l > A[i]) {
largest = l;
}
if(r <= A.heap-size and r > A[largest]) {
largest = r;
}
if(largest != i) {
exchange(A, i, largest);
MAX-HEAPIFY[A, largest];
}
}
3.4 构建最大堆
3.4.1 思想
我们发现对于一个数组A(1,n)的数组,我们发现子数组A(n/2 + 1, n)是都是堆的叶子节点。我们可以自低向上的调用MAX-HEAPIFY就可以构建一个堆。
3.4.2 演示图
3.4.3 伪代码
BUILD-MAX-HEAP(A) {
for(i = A.length / 2 down to 1) {
MAX-HEAPIFY(A, i);
}
}
3.5 堆排序算法
3.5.1 思想
我们发现最大堆的根节点A[1]一定是数组中最大的元素,那么我们替换A[1]和A[n],让最大节点放置在数组最后,然后再调用MAX-HEAPIFY[1],重新维护堆,然后依次轮推。
3.5.2 演示图
3.5.3 伪代码
HEAPSORT(A {
BUILD-MAX-HEAP(A);
for(i = A.length downto 2) {
exchange(A, 1, i);
A.heap-size = A.heap-size - 1;
MAX-HEAPIFY(A,1);
}
}
4.快速排序
4.1 思想
快速排序的思想是,每一次排序,都是的数组A[p,r]被划分为两个子数组A[p,q-1],A[q+1,r],使得A[p,q-1]中所有的元素都比A[q]小,A[q+1,r]中的所有的元素都比A[q]大,然后再用这个想法递归的对两个子数组进行排序。
4.2 示意图
4.3 伪代码
QUICKSORT(A, p, r) {
if(p < r) {
q = PARTITION(A, p, r)
QUICKSORT(A, p, q - 1)
QUICKSORT(A, q + 1, r)
}
}
PARTITION(A, p, r) {
x = A[r];
i = p - 1;
for(j = p to r - 1) {
if(A[j] < x) {
i = i + 1;
exchange(A, i, j);
}
}
exchange(A, i + 1, r)
return i + 1;
}
4.4 思考快速排序最坏的情况以及解决办法
5 思考
比较算法的最优的时间复杂度。
5.1 决策树
6 计数排序
6.1 思想
假设n个需要排序的元素,每个元素都在0~k中,那么对于每一个元素x,我们只要确定了小于x的元素个数,就可以确定x的位置。
6.2 示意图
6.3 伪代码
COUNTING-SORT(A, B, k) {
new C[0, k];
//第一步 初始化C
for(i = 0 to k) {
C[i] = 0;
}
//第二步 统计各个元素的个数
for(j = 1 to A.length) {
C[A[j]] = C[A[j]] + 1;
}
//第三步 统计各个元素对应的比之小的元素个数
for( i = 1 to k) {
C[i] = C[i-1] + [i];
}
//第四步 将各个元素放置相应的位置
for(j = A.length downto 0) {
B[C[A[j]]] = A[j];
C[A[j]] = C[A[j]]-1;
}
}
6.4思考
第三步为啥要倒序?
7. 基数排序
7.1 思想
把待排序的整数先按个位进行排序,再按照十位排序。。。最后得到排序好的数组
7.2 示意图
7.3 伪代码
//sdasd asdas
RADIX-SORT(A, B, maxDigit) {
final int radix = 10
new C[0, radix];
for(int d = 1 to maxDigit) {
//初始化C,置零
INIT(C);
for(j = 1 to A.length) {
int digit = GET_DIGIT[A[j], d];
C[digit] = C[digit] + 1;
}
for (i = 1 to radix) {
C[digit] = C[i - 1] + C[i];
}
for(j = A.length downTo 1) {
int digit = GET_DIGIT[A[j]];
B[C[digit]] = A[j];
C[digit] = C[digit] - 1;
}
for () {
//把B数组的值复制给A
//结果就是A是以个位数排序后的结果
}
}
}
8. 桶排序
8.1 想法
假设所有需要排序的数组A[n]服从均匀分布,且所有的数都在一个小的整数区间内。
8.2 示意图
8.3 伪代码
BUCKET_SORT(A) {
n = A.length;
new B[0, n-1];
for(i = 0 to n - 1) {
B[i] = EMPTY_LIST;
}
for(i = 1 to n) {
INSERT A[i] INTO LIST B[nA[i]]
}
for(int i = 0 to n - 1) {
INSERTION_SORT(B[i])
}
CONCATENATE B;
}
9 思考
- 查找数组中的最大的数
- 同时查找数据的最大数和最小数
- 查询第N大的数
- map reduce 思想
- bitMap 布隆过滤(缓存穿透)