目录
一、插入排序
1.1 简单插入排序
- 插入排序核心思想:将待排序的一组序列分成两组:已排序的组和未排序的组。初始时,选取第一个元素加入已排序的组,其余元素全部放在未排列的组。进行排序时,从未排序的组中取出一个元素,按顺序插入待已排序的组中。循环该过程,直至所有元素均被排序。
- 代码:
//小值在前
void InsertSort(vector<int>& v){
if(v.size() <= 1)return;
int lastIndex;
//这个循环是遍历序列的每个元素
for(int i = 1; i < v.size(); i++){
int tmp = v[i];
//这个循环是将第i个未排序元素tmp往前对比,直至全部对比完或碰到比这个元素tmp小的
for(lastIndex = i; lastIndex > 0 && v[lastIndex - 1] > tmp; lastIndex--){
v[lastIndex] = v[lastIndex-1];
}
v[lastIndex] = tmp;
}
}
- 复杂度分析:时间复杂度:两层for,最坏情况能到O(n^2), 最好情况就是已经排好序,不用进入第二个for,相当于遍历数组,复杂度为O(n)。空间复杂度:...貌似没怎么额外申请空间,就一两个变量而已,空间复杂度O(1)
1.2 希尔排序
- 核心思想:插入排序低效的原因是每次比较只能交换相邻两个元素的位置,希尔排序是多做几次插入排序,但每次插入排序的元素是隔着一定距离的,最后一次的插入排序才是间隔为1的正常插入排序。这样虽然多做了几次排序,但经过前面几次的排序后,序列已经大致有序,最后一次插入排序时的,每次插入需要移动元素的个数会大幅度降低。
代码:
-
//小值在前 void ShellSort(vector<int>& v){ if(v.size() <= 1)return; vector<int>increase_array = {1000, 100, 10, 5, 1}; //获取适合大小的初始增量大小,不能超出数组长度 int increase_index; for(increase_index = 0; increase_array[increase_index] > v.size(); increase_index++); int lastIndex; for(int d = increase_array[increase_index]; increase_index < increase_array.size(); d = increase_array[++increase_index]){ //这个循环是遍历序列的每个元素 for(int i = d; i < v.size(); i++){ int tmp = v[i]; //这个循环是将第i个未排序元素tmp往前对比,直至全部对比完或碰到比这个元素tmp小的 for(lastIndex = i; lastIndex > d - 1 && v[lastIndex - d] > tmp; lastIndex--){ v[lastIndex] = v[lastIndex-d]; } v[lastIndex] = tmp; } } }
- 复杂度分析:玄学算法,时间复杂度据说还没定论,空间复杂度为O(1)
二、比较排序
2.1 堆排序(这里介绍最大堆排序)
- 核心思想:最大堆的根节点一定是整个堆的最大值,利用这一特点,如下处理:(1)将堆的根节点与堆数组的最后一个节点交换;(2)重新对堆进行维护,在维护过程中,将已经被移动的原来堆的根节点视为已不属于这个堆,即维护一个少了一个节点的堆,此时,堆顶节点为次大元素;(3)将当前堆顶节点与倒数第二个节点交换位置。重复上述操作至当前堆只剩一个节点为止。
- 代码:
void MaxHeapSort(vector<int>& v){
if(v.empty())return;
for(int n = 0; n < v.end() - v.begin() - 1; n++){
make_heap (v.begin(), v.end() - n);
pop_heap(v.begin(), v.end() - n);//标准库函数,直接帮我们把堆的根节点放到数组的最后一个元素,且剩余节点重排
}
}
- 时间复杂度:时间复杂度O(nlogn)、时间复杂度O(1)
2.2 冒泡排序
- 当年学C的时候写过...不想写了,偷个懒~
三、递归排序
3.1 归并排序
- 核心思想:假设目标序列由两个已排序的子序列组成,则这两个子序列边排序边合并,就可以对整个序列进行排序了;利用递归思想,可以知道当子序列中只有一个元素时的排序是最简单的,因此整个归并是用递归来实现的。注意,在进行合并的时候,要利用一个辅助数组来暂存合并结果。
- 代码:
///合并两个序列 void combine(vector<int>& A, vector<int>& tmp, int left, int right, int right_end){ int left_end = right - 1; int i = left, j = right; int index = left; //先将一部分合并 for(;i <= left_end && j <= right_end;){ if(A[i] < A[j])tmp[index++] = A[i++]; else tmp[index++] = A[j++]; } //将剩下的部分也放进去 while(i <= left_end){ tmp[index++] = A[i++]; } while(j <= right_end){ tmp[index++] = A[j++]; } //将临时序列中的序列传给原序列 for(index = left; index <= right_end; index++){ A[index] = tmp[index]; } } ///递归程序 void merge_sort(vector<int>& A, vector<int>& tmp, int left, int right_end){ if(left < right_end){ int center = (right_end + left) / 2; merge_sort(A, tmp, left, center); merge_sort(A, tmp, center + 1, right_end); combine(A, tmp, left, center+1, right_end); //这几个left、right、right_end一定要想清楚传什么,不然程序会出错 } } ///驱动程序 void merge_sort(vector<int>& v){ auto tmp = v; tmp.assign(tmp.size(), 0); merge_sort(v, tmp, 0, v.size() - 1); }