一、基本思想
快速排序的基本思想都是大的数据往后放,小的数据往前放,key放在对应的正确的位置上,且由key划分的区间两边数据所在区间的位置都是正确
二、递归实现
1.方法一:双向扫描法
两边同时扫描,begin遇到比key大的停下来,而end遇到比key小的停下来,然后两者交换,直到两个指针相遇(注意两个元素组成的有序区间的情况)
void QuickSort(int arr[], int left,int right)
{
assert(arr);
if (left >= right)
return;
int div = PartSort1(arr, left, right);
QuickSort(arr, left, div - 1);
QuickSort(arr, div + 1, right);
}<pre name="code" class="cpp">
//(1)大的数据往后翻,小的数据往千帆,key在正确的位置上
int PartSort1(int arr[], int left, int right)//返回key位置对应的下标
{
int key = arr[right];
int begin = left;
int end = right - 1;
while (begin < end)
{
while (begin<end&&arr[begin] <= key)
{
++begin;
}
while (begin < end&&arr[end]>=key)
{
--end;
}
if (begin < end)
{
swap(arr[begin], arr[end]);
}
}
if (arr[begin]>arr[right])//由于begin先,所以begin所在的位置一定是大于key值所在的位置
{
swap(arr[begin], arr[right]);
return begin;
}
else//两个元素组成的区间有序
{
return right;
}
}
//(1)大的数据往后翻,小的数据往千帆,key在正确的位置上
int PartSort1(int arr[], int left, int right)//返回key位置对应的下标
{
int key = arr[right];
int begin = left;
int end = right - 1;
while (begin < end)
{
while (begin<end&&arr[begin] <= key)
{
++begin;
}
while (begin < end&&arr[end]>=key)
{
--end;
}
if (begin < end)
{
swap(arr[begin], arr[end]);
}
}
if (arr[begin]>arr[right])//由于begin先,所以begin所在的位置一定是大于key值所在的位置
{
swap(arr[begin], arr[right]);
return begin;
}
else//两个元素组成的区间有序
{
return right;
}
}
2、方法二:挖坑法
本质上与双向扫描法相同,但是这次是去挖一个坑,准备埋left和right遍历过程中需要停止的那个点,进行交换,只不过这个方法原有的key的值被覆盖了,而上一个方法中key的值最后才交换
void QuickSort(int arr[], int left,int right)
{
assert(arr);
if (left >= right)
return;
int div = PartSort2(arr, left, right);
QuickSort(arr, left, div - 1);
QuickSort(arr, div + 1, right);
}
//2、挖坑法[left,right]
int PartSort2(int arr[], int left, int right)
{
int key = arr[right];
while (left < right)
{
while (left < right&&arr[left] <= key)
{
++left;
}
if (left < right)
{
arr[right--] = arr[left];
}
while (left<right&&arr[right]>=key)
{
--right;
}
if (left < right)
{
arr[left++] = arr[right];
}
}
arr[left] = key;//<span style="color:#ff0000;">注意是left</span>
return left;
}
3、方法三:单向扫描法
就是用prev标记比key小的数字区间的最后一个元素的下标,然后不断遍历,不断扩大小的数字的区间,最后将key的值放入到小的数的区间的最后一个元素的下一个位置
void QuickSort(int arr[], int left,int right)
{
assert(arr);
if (left >= right)
return;
int div = PartSort3(arr, left, right);
QuickSort(arr, left, div - 1);
QuickSort(arr, div + 1, right);
}
//3.单向扫描法
//(思想相同但是这个可以给单链表快速排序,因为不同倒着遍历链表)
int PartSort3(int arr[], int left, int right)
{
int key = arr[right];
int prev = left - 1;//指向大的数据前一个位置,用来换大
int cur = left;//用来找小
while (cur < right)
{
if (arr[cur] < key&&++prev != cur)
{
std::swap(arr[prev], arr[cur]);
}
++cur;
}
std::swap(arr[++prev], arr[right]);
return prev;
}
三、优化方法
1、三数取中法
上述实现的代码key都是选取最后一个元素,当然也可以选取第一个元素。
快速排序当序列本身是有序的时候是最坏的情况,时间复杂度为O(N^2),当每次选取的key恰好在中间时是最优的,时间复杂度为log2N
所以使用三数取中法进行优化,防止有序是最坏的情况
//优化;三数取中法
int GetMinIndex(int arr[], int left, int right)
{
int mid = left + (right - left) / 2;
if (arr[left] < arr[mid])
{
if (arr[left] < arr[mid])
{
return mid;
}
else if (arr[left]<arr[right])//left<mid&&right<mid
{
return right;
}
else
{
return left;
}
}
else //mid<left
{
if (arr[left] < arr[right])
{
return left;
}
else if (arr[mid] < arr[right])//mid<left&&right<left
{
return right;
}
else
{
return mid;
}
}
}
int PartSort3_MIN(int arr[], int left, int right)
{
int midIndex = GetMinIndex(arr, left, right);
if (midIndex != right)
{
std::swap(arr[midIndex], arr[right]);//<span style="color:#ff0000;">快速排序一定要选取左右作key</span>
}
int key = arr[right];
int prev = left - 1;//指向大的数据前一个位置
int cur = left;
while (cur < right)
{
if (arr[cur] < key&&++prev != cur)
{
std::swap(arr[prev], arr[cur]);
}
++cur;
}
std::swap(arr[++prev], arr[right]);
return prev;
}
2、减少递归次数,
当要排序的区间[left,right]比较小的时候,这个时候意味这递归的深度也比较深,这个时候就有可能造成栈溢出,所以这个时候用插入排序代替快速排序进行优化
void InsertSort(int arr[],int left, int right)//[left,right]
{
if (arr == NULL || right-left+1<= 0)
return;
for (int i = left; i < right; i++)
{
int end = i;//一个有序序列的结尾
int tmp = arr[i + 1];
while (end >= 0 && arr[end]>tmp)
{
arr[end + 1] = arr[end];
end--;
}
arr[end + 1] = tmp;
}
}
void QuickSort_Insett(int arr[], int left, int right)
{
assert(arr);
if (left >= right)
return;
if (right - left < 13)
{
InsertSort(arr, left,right);
}
int div = PartSort3_MIN(arr, left, right);
QuickSort(arr, left, div - 1);
QuickSort(arr, div + 1, right);
}
三、非递归实现
用栈进行处理,每次都压两个边界
//非递归实现
void QuickSort_NonR(int arr[], int left, int right)
{
assert(arr);
stack<int> s;
if (left < right)
{
s.push(right);
s.push(left);
}
while (!s.empty())
{
left = s.top();
s.pop();
right = s.top();
s.pop();
if (right - left < 13)//库中给的是13,减少压栈
{
InsertSort(arr, left,right);
}
else
{
int div = PartSort3_MIN(arr, left, right);
if (left < div - 1)
{
s.push(div - 1);
s.push(left);
}
if (div < right)
{
s.push(right);
s.push(div + 1);
}
}
}
}