赞
踩
整体思路:
每次固定一个数的位置
默认当前范围的第一个数就是要固定的数
比它大的放左边,比它小的放右边
每次固定好一个数的位置后,去左区间固定一个数,右区间固定一个数,以此类推,直到要固定数的范围只剩一个数或范围不存在就停止
begin == end表示这个范围只有一个数,begin>end表示范围不存在
PartSort这个函数的功能是:从当前范围中固定好一个数的位置,并返回固定后的位置下标,具体如何实现这个功能,下面右三种方法
void QuickSort(int* a, int begin, int end)
{
if(begin>=end)
return;
int keyi = PartSort(a,begin,end);
QuickSort(a,begin,keyi-1);
QuickSort(a,keyi+1,end);
}
根据上面步骤,演示代码
//haore int PartSort1(int* a, int left, int right) { int keyi = left; while (left < right) { //右边找小 while (a[right] > a[keyi]) { right--; } //左边找大 while (a[left] < a[keyi]) { left++; } Swap(&a[left], &a[right]); } Swap(&a[keyi], &a[left]); keyi = left; return keyi; }
如果左右两边都有等于a[keyi]的值,这时left和right都会停下,然后交换,交换后继续找大或找小,但left和right仍然不动,继续交换,一直重复这个操作,成为死循环
解决方法:
遇到等于a[keyi]就继续++/–
右边找小的条件:a[right]>=a[keyi]
左边找大的条件:a[left]<=a[keyi]
极端情况下,这个范围里的值刚好是升序的,right先走,一直找小,找不到,来到了left的位置,依然没找到,所以right继续走,走到了left前面的位置,此时right就已经越界,Swap(&a[left],&a[right])时就会非法访问
解决方法:
right和left每次找值的时候先判断是否已经相遇,若相遇就不今小循环
增加的条件left<right
修改后的代码:
//haore int PartSort1(int* a, int left, int right) { int keyi = left; while (left < right) { //右边找小 while (left < right && a[right] >= a[keyi]) { right--; } //左边找大 while (left < right && a[left] <= a[keyi]) { left++; } Swap(&a[left], &a[right]); } Swap(&a[keyi], &a[left]); keyi = left; return keyi; }
两种方案:
根据方案1分情况讨论:
右边先走:L遇R,R遇L
//挖坑法 int PartSort2(int* a, int left, int right) { int key = a[left];//先将要固定的值保存起来 int hole = left; while (left < right) { //右边找小 while (left < right && a[right] >= key) { right--; } //更新坑的位置 a[hole] = a[right]; hole = right; //左边找大 while (left < right && a[left] <= key) { left++; } //更新坑位置 a[hole] = a[left]; hole = left; } //最后坑的位置就是要固定的值的位置 a[hole] = key; return hole; }
//前后指针法 int PartSort3(int* a, int left, int right) { int prev = left; int cur = left + 1; int keyi = left; while (cur <= right) { //cur找小,与++prev交换 if (a[cur] < a[keyi] && ++prev != cur)//++prev != cur是一点优化,针对prev和cur之间没有大于a[keyi]的值时的原地交换 { Swap(&a[prev], &a[keyi]); } //if (a[cur] < a[keyi]) //{ // ++prev; // Swap(&a[prev], &a[keyi]); //} //无论a[cur]大于小于还是等于a[keyi],cur都++ cur++; } //prev指向的是所有大于a[keyi]的前一个位置 Swap(&a[prev], &a[keyi]); keyi = prev; return keyi; }
用栈模拟递归的过程
//快排非递归方法 void QuickSortNonR(int* a, int left, int right) { ST st; STInit(&st); //将左右区间压入栈中 STPush(&st, right); STPush(&st, left); while (!STEmpty(&st)) { //弹出左右范围 int leftrange = STTop(&st); STPop(&st); int rightrange = STTop(&st); STPop(&st); //在这个范围中固定好一个值 int keyi = PartSort1(a, leftrange, rightrange); //先压右后压左区间是为了模拟递归的调用过程 //如果keyi的右区间的个数至少大于一个就说明还需要固定值,所以压栈 if (keyi + 1 < rightrange) { STPush(&st, rightrange); STPush(&st, keyi + 1); } //如果keyi的做区间的个数至少大于一个就说明还需要固定值,所以压栈 if (keyi - 1 > leftrange) { STPush(&st, keyi - 1); STPush(&st, leftrange); } } STDestory(&st); }
最好的情况,每次要固定的位置都在最中间,这样的时间复杂度是O(N*logN)
最坏的情况,要排的本来就是升序,每次要固定的位置都在最左边,时间复杂度是O(N2)
时间复杂度与选的key是有关的,这个key越靠近中间位置,时间复杂度越小
解决方案:
由于快排的时间复杂度跟每次选的key值有关,如果每次固定的值刚好是最小值,那么快排的速度就会非常慢,几乎是O(N2)级别
这种情况的例子就是有序
那么如何在有序或接近有序的情况让快排的速度提高呢?
三数取中
在当前范围中选取左边界,中间和右边界三个数,比较大小,将中间的值作为key,与左边界的值进行交换,然后再进行固定key的操作
//快排的优化:三数取中 int GetMidIndex(int* a, int left, int right) { int mid = left + (right - left) / 2; if (a[left] > a[mid]) { if (a[mid] > a[right]) { return mid; } else if (a[left] > a[right]) { return right; } else //a[left]>a[mid], a[left]<a[right] { return left; } } else //a[mid]>a[left] { if(a[right]>a[mid]) { return mid; } else if (a[right] > a[left]) { return right; } else //a[mid]>a[left], a[left]>a[right] { return left; } } }
当要排的数据含有大量重复的值甚至全部一样,我们的快排就会被克制,
每次确定一个值的位置,但按照上图的过程进行下去,最后的时间复杂度到达O(N2),为了保证遇到这类情况也会很快的解决,使用三路划分即可
思路:
将小于key的值弄到左侧,大于key的值弄到右侧,等于key的值弄到中间,这样排一次就可以固定好多个等于key值的值
三个指针l,r,cur;l指向左边界,r指向右边界,cur指向l的下一个位置
步骤:
分析:
本质:
代码实现:
//快排的优化:三路划分 void QuickSort2(int* a, int begin, int end) { if (begin >= end) { return; } int l = begin; int r = end; int cur = begin + 1; int key = a[begin]; while (cur <= r) { if (a[cur] < key) { Swap(&a[cur], &a[l]); cur++; l++; } else if (a[cur] > key) { Swap(&a[cur], &a[r]); r--; } else { cur++; } } QuickSort2(a, begin, l - 1); QuickSort2(a, r + 1, end); }
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。