排序算法总结
1、排序算法稳定性
稳定性的概念远没有这么复杂,它只表示两个值相同的元素在排序前后是否有位置变化。如果前后位置变化,则排序算法是不稳定的,否则是稳定的。稳定性的定义符合常理,两个值相同的元素无需再次交换位置,交换位置是做了一次无用功。主要看是否有等于=。
为什么排序算法最差情况时间复杂度是O(N^2):
pivot选择是在总是在第一个位置,T(N) = T (N - 1) + T (0) + O(N);
排序法 | 平均时间 | 最差情形 | 稳定度 | 额外空间 | 备注 |
冒泡 | O(n2) | O(n2) | 稳定 | O(1) | n小时较好 |
交换 | O(n2) | O(n2) | 不稳定 | O(1) | n小时较好 |
选择 | O(n2) | O(n2) | 不稳定 | O(1) | n小时较好 |
插入 | O(n2) | O(n2) | 稳定 | O(1) | 大部分已排序时较好 |
基数 | O(logRB) | O(logRB) | 稳定 | O(n) |
B是真数(0-9), R是基数(个十百) |
Shell | O(nlogn) | O(ns) 1<s<2 | 不稳定 | O(1) | s是所选分组 |
快速 | O(nlogn) | O(n2) | 不稳定 | O(nlogn) | n大时较好 |
归并 | O(nlogn) | O(nlogn) | 稳定 | O(1) | n大时较好 |
堆 | O(nlogn) | O(nlogn) | 不稳定 | O(1) | n大时较好 |
2、堆排序介绍
二叉树:
层数从0开始,那么第i层最多有 2^i 个节点,如果二叉树深度为k,那么总节点数为2 ^ (k + 1) - 1,最底层的节点数n2,其他层节点数为n0,那么n2 = n0 + 1;
堆:
使用的是完全二叉树,对应于一个vector实现,有最大堆和最小堆。
下标从1开始的话,对应关系是:
对于给定的某个结点的下标 i,可以很容易的计算出这个结点的父结点、孩子结点的下标:
- Parent(i) = floor(i/2),i 的父节点下标
- Left(i) = 2i,i 的左子节点下标
- Right(i) = 2i + 1,i 的右子节点下标
下标从0开始的,对应关系是:
- Parent(i) = floor((i-1)/2),i 的父节点下标
- Left(i) = 2i + 1,i 的左子节点下标
- Right(i) = 2i + 2,i 的右子节点下标
#include<iostream> #include<vector> using namespace std; //heapsort void siftdown(vector<int> &vec, int i, int len) { int l = 2 * i + 1; int r = 2 * i + 2; int largest = i; if (l < len && vec[l] > vec[i]) { largest = l; } if (r < len && vec[r] > vec[largest]) {//这里总是写成l,记住是比较后的largest largest = r; } if (largest != i) { swap(vec[i], vec[largest]); siftdown(vec, largest, len);//交换之后原来以i作为root,它的left和right都小于root, //满足要求,所以要递归到交换后的那个root,即largest } } void heapsort(vector<int> &vec) { if (vec.size() == 0) { return; } //创建一个最大堆 for (int i = vec.size() / 2; i >= 0; --i) {//计算非叶子节点的过程中[0~len / 2],就会顺便把后面排序。 siftdown(vec, i, vec.size()); } //堆排序 for (int i = vec.size() - 1; i >= 0; --i) { swap(vec[0], vec[i]); siftdown(vec, 0, i);//这里的i不包括最后一个元素了,因为已经将最大元素交换到后面了 } }
//down to up #include<iostream> #include<vector> using namespace std; void siftdown(vector<int> &vec,int i,int sz) { int l = 2 * i + 1; int r = 2 * i + 2; int largest = i; if (l < sz && vec[l] > vec[i]) { largest = l; } if (r < sz && vec[r] > vec[largest]) { largest = r; } if (largest != i) { swap(vec[i], vec[largest]); siftdown(vec, largest, sz); } } void heapsort(vector<int> &vec) { for (int i = vec.size() / 2; i >= 0; --i) { siftdown(vec, i, vec.size()); } for (int i = vec.size() - 1; i >= 0;--i) { swap(vec[0], vec[i]); siftdown(vec, 0, i);//这里的i每次运行后最后一个元素就是不需要的,所以不需要i + 1; } } int main() { vector<int> vec{ 3,5,2,6,68,9 }; heapsort(vec); for (int num : vec) { cout << num << " "; } system("pause"); }
归并思路:
1、序列一分为二;
2、子序列递归排序;
3、合并有序子序列。
必须要开一个暂存数组,保存中间排序元素。
#include<vector> #include<iostream> using namespace std; void merge(vector<int>& vec, int lo, int mid, int hi) { //这里是传入引用,才能每次修改 vector<int> tempvec{vec};//每次拷贝vec注意点,如果写成tmpvec(vec.size(),0)则每次递归调用初始化这里就出错了,变为全为0,
//每一层递归调用都会开辟一个该递归层的tmp,而vec是传引用的形式,所以每层都一样 int leftend = mid, rightend = hi; int lpos=lo,rpos=mid+1;
int temp=lo; while ((lpos <= leftend) && (rpos <=rightend )) {//compare two arrays,copy other numbers if (vec[lpos] <= vec[rpos]) tempvec[temp++] = vec[lpos++]; else tempvec[temp++] = vec[rpos++]; } while (lpos <=leftend) tempvec[temp++] = vec[lpos++]; while (rpos <= rightend ) tempvec[temp++] = vec[rpos++]; for (int iz = 0; iz <tempvec.size(); ++iz){ vec[iz] = tempvec[iz]; } } void mergesort(vector<int>& vec, int lo, int hi) {//recursion int mid; if ( lo < hi ) {//这里如果写相等的话会陷入死循环 mid = (lo + hi) / 2; //每步都执行完毕后再执行下一句,然后再倒着执行回来; mergesort(vec, lo, mid); mergesort(vec, mid+1 , hi); merge(vec, lo, mid, hi); } } int main() { vector<int> vec{ 3,2,4,5,2 }; int n = vec.size() - 1; mergesort(vec, 0, n); for (int iz = 0; iz <=n; ++iz) cout<< vec[iz]<<" "; cout << endl; }
快速排序:
#include<iostream> #include<vector> using namespace std; int partition(vector<int>& seq, int lo, int hi) {//pivot右侧的都大,左侧的都小 int rNum = lo + rand() % (hi - lo + 1);//找一个随机位置 int pivot = seq[rNum];//先找锚点 swap(seq[lo], seq[rNum]); while (lo < hi) {//左右交差进行查找 /*先从右向左查找第一个小于pivot的元素*/ while (lo < hi) { if (pivot < seq[hi]) --hi; else { seq[lo] = seq[hi]; ++lo; break; } } /*从左向右查找第一个大于pivot的元素*/ while (lo < hi) { if (pivot > seq[lo]) ++lo; else { seq[hi] = seq[lo]; --hi; break; } } } seq[lo] = pivot;//记得将该点放到目的位置 return lo; } void quicksort(vector<int>& seq,int lo,int hi) { /*if (hi - lo < 1)*/ if (hi < lo) return; int mid = partition(seq,lo,hi); quicksort(seq,lo,mid-1); quicksort(seq,mid+1,hi); } int main() { vector<int> seq{ 2,1,4,2,1 }; quicksort(seq, 0, seq.size() - 1); for (unsigned int i = 0; i < seq.size(); ++i) cout << seq[i] << " "; cout << endl; }