数据结构排序入门(2):核心排序(选择排序,快速排序及优化)
1.选择排序
思想:遍历数组,选出最小的,插入到前面,接着不断遍历选出第二小,第三小,有序放在前面
优化:遍历数组,选出最小的和最大的,最小的插到前面,最大的插入到最后面,接着选出第二小和第二大,有序交换插入进去。
//选择排序
void SelectSort(int* a, int n)
{int begin = 0, end = n - 1;while (begin < end)//后续end会不断向前减,begin会不断向后挪,end--,begin++//最终会相遇,所以while循环的结束条件是begin<end{int mini = begin, maxi = begin;//都从头开始遍历,找出最小和最大的for (int i = begin + 1; i <= end; ++i){//i从begin+1 的位置开始向后加,第一次的begin是下标为0的首元素,从第二个元素开始比较if (a[i] > a[maxi]){maxi = i;}if (a[i] < a[mini]){mini = i;}}Swap(&a[begin], &a[mini]);if (begin == maxi)maxi = mini;Swap(&a[end], &a[maxi]);++begin;--end;}
}
2. 快速排序(hoare版本)
void QuickSort(int* a, int left, int right)
{if (left >= right)return;//什么时候出现left>=right的情况?//先写单趟int keyi = left;int begin = left, end = right;while (begin > end){while (a[begin] <= a[keyi]&& begin > end){begin++;}while (a[end] >=a[keyi]&& begin > end){end--;}//两个while中都是有序的情况,没有排第一组和第二组Swap(&a[begin], &a[end]);//这里才是第一组和第二组交换位置}//已经出了循环,begin和end相遇Swap(&a[end], &a[keyi]);//将3和6的位置进行交换keyi = begin;//数据进行交换//排好一个数,分割出左区间右区间,递归的过程跟前序是一样的// [left,keyi-1] keyi [keyi+1,right]//进行递归QuickSort(a, left, keyi - 1);QuickSort(a, keyi + 1,right);
}
2.1 重点:哪种情况是left>right的?
快排代码详解怎么写出来的?一步一步分析
相遇的场景分析:
L遇R:R先走,停下来,R停下的条件是遇到比key小的值,R停止的位置一定比key小, L没有找大的,遇到R停下了
R遇L:R先走,找小,没有找到比key小的,直接跟L相遇了,L停止的位置是跟上一轮交换的位置,上一轮交换,把比key小的值,换到L的位置了
左边做key,右边先走,可以保证相遇位置比key要小;
相反,如果让右边做key,左边先走,可以保证相遇位置比key要大
快排递归过程分析:
2.2 优化挖坑法:
int QuickSort2(int* a, int begin, int end)
{//begin是坑int key = a[begin];while (begin < end){while (begin < end && a[end] >= key)--end;// end给begin这个坑,end就变成了新的坑。a[begin] = a[end];while (begin < end && a[begin] <= key)++begin;// end给begin这个坑,begin就变成了新的坑。a[end] = a[begin];}a[begin] = key;return begin;
}
优化在哪:挖坑法没有效率的提升,但是不用分析,左边做key,右边先走的问题,也不用分析,相遇位置为什么比key小的问题,因为它的相遇位置是坑。
2.3 前后指针法!
// 前后指针
int PartSort2(int* a, int left, int right)
{int keyi = left;int prev = left;int cur = prev+1;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur)Swap(&a[prev], &a[cur]);cur++;}Swap(&a[prev], &a[keyi]);return prev;
}void QuickSort(int* a, int left, int right)
{if (left >= right)return;int keyi = PartSort2(a, left, right);// [left, keyi-1] keyi [keyi+1, right]QuickSort(a, left, keyi - 1);QuickSort(a, keyi + 1, right);
}
2.4 快排非递归
快排非递归,深度太深的情况下,递归会有溢出的风险,把数据用栈来模拟,最开始将0~9存到栈里面,进行单趟排序,左右区间排序好之后压栈,循环每走一次,取栈顶区间,单趟排序,右左区间入栈
循环每走一次,相当于一次递归
//非递归快排
#include"Stack.h"
void QuickSortNonR(int* a, int left, int right)
{//入栈ST st;STInit(&st);STPush(&st, right);STPush(&st, left);while (!STEmpty(&st)){int begin = STTop(&st);STPop(&st);int end = STTop(&st);STPop(&st);int keyi = PartSort2(a, begin,end);if (keyi + 1 < end){STPush(&st, end);STPush(&st, keyi + 1);}if (begin < keyi-1){STPush(&st, begin);STPush(&st, keyi - 1);}}STDestroy(&st);
}