5.QuickSort快速排序
int GetKey(int *a,int left,int right){//取到中间值
int mid=(left+right)/2;
if (a[right] > a[left]) {
if (a[mid] > a[right])return right;
else if (a[mid] < a[left])return left;
else return mid;
}
else {
if (a[mid] > a[left])return left;
else if (a[mid] < a[right])return right;
else return mid;
}
}
void QuickSort(int* s,int left,int right,int n){
if(left>=right)return ;
int begin=left,end=right;
int mid= GetKey(s,left,right);
Swap(&s[mid], &s[begin]);//将取得的mid与begin位交换
int pivot=begin;
int key=s[begin];//把开始位置作为坑空出,并用key保存中间值
while(begin<end){
while (end > begin) {
if (s[end] < key) {
s[pivot] = s[end];
pivot = end;
break;
}
end--;
}
while (begin < end) {
if (s[begin] > key) {
s[pivot] = s[begin];
pivot = begin;
break;
}
begin++;
}
}
s[pivot]=key;
QuickSort(s,left,pivot-1,n);
QuickSort(s,pivot+1,right,n);
}
[1]原理
GetKey得到的是该数组首尾及中间三个数的中间值(防止起始位过小或过大提高排序效率)。之后初始设置pivot为首位(begin),每次大循环分别从尾首判断与比较数key的大小关系,并将符合条件的数放到坑中同时该位置变为坑,循环结束后将key放入最后的坑中。每次操作将对应的key放在操作的那段数组的”中间“(key左边都小于key,可以右边都大于key)。如此递归后最终完成排序。
以下以{3,1 ,5,2,4}为例:
之后同理,最终得到{1,2,3,4,5}
[2]时间复杂度
与归并排序类似,都是分治算法!
[3]拓展方法*
其他方法将中间数放在数组中间。
(1)左右指针
int swapsort(int * a, int n, int left, int right) {//左右指针法(交换)
int begin = left, end = right;
int mid = GetKey(a, n, left, right);
swap(&a[mid], &a[begin]);//将取得的mid与begin位交换让key取到a[mid]
int i = 0;
int keyi = begin;//以左边为key为例
while (begin < end) {
//找小
while (begin < end && a[end] >= a[keyi]) {
end--;
}
//找大
while (begin < end && a[begin] <= a[keyi]) {
begin++;
}
Swap(&a[begin], &a[end]);
}
Swap(&a[begin], &a[keyi]);
return begin;//end
}
(2).前后指针
int front_laterSort(int* a, int n, int left, int right) {//前后指针法
int mid = Getkey(a, n, left, right);
Swap(&a[mid], &a[left]);//将取得的mid与begin位交换让key取到a[mid]
int i = 0;
int keyi = left;//以左边为key为例
int cur = left + 1, prev = left;
while (cur <= right) {
if (a[cur] < a[keyi]) {
++prev;
swap(&a[prev], &a[cur]);
}
cur++;
}
Swap(&a[prev], &a[keyi]);
return prev;
}
6.堆排序
void adjust(int * a, int n, int root) {//向下调整算法(从根节点开始,前提是左右子树为大堆(小堆)),以大堆算法为例
int parent = root;
int child = parent * 2 + 1;//下标从零开始,默认为左孩子
while (child<n) {//到“叶节点”就终止
if (child+1<n&&a[child + 1] > a[child]) {//选出较大的孩子节点赋给child,可能有右孩子不存在的情况
child++;
}
if (a[child] > a[parent]) {//较大的如果比父节点还大则交换两者
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;//更新parent和child
}
else break;//没找到,结束
}
}
void HeapSort(int * a,int n) {//堆排序(本质为选择排序),升序用大堆
//把数组建成(大)堆
int i = 0;
for (i = (n - 1 - 1) / 2; i >= 0; i--) {//从最底端开始依次向上构建大堆,时间复杂度为O(n)
adjust(a, n, i);
}
int last = n - 1;
while (last >= 0) {//将大堆的首元素和尾元素交换
Swap(&a[0], &a[last]);
adjust(a, last, 0);//再把最大的取出来
last--;
}
}
[1]原理
最后排序类似于选择排序,但堆排序的处理更有效率,将数组看作一棵二叉树,通过下标访问各个节点。
主要原理如下图:
[2]时间复杂度
7.各种排序算法时间复杂度比较:
书接上回, 依旧是10^5的数量级。。。(快排真的很快)