快速排序是交换排序的一种,这是因为快速排序的核心还是交换两个合适的元素。快排有很多实现方法,类似更新迭代,但是核心思想都一样。Hoare法,挖坑法,前后指针法,非递归法(栈实现,队列实现) 下面一一介绍。
目录
快速排序,Hoare版本:
Hoare 左闭右闭
void QuickSort(int* arr, int begin, int end) { // [begin, end]
int left = begin;
int right = end;
int keyi = begin; // 设置下标是因为后面要交换数据
while(left < right) {
// 右边找小
while(left < right && arr[right] >= arr[keyi]) {
right--;
}
// 左边找大
while(left < right && arr[left] <= arr[keyi]) {
left++;
}
// 这里不管是否left<right都交换,等于也无妨。
swap(arr[left], arr[right]);
}
// 这里出循环之后,一定符合left == right。
swap(arr[keyi],arr[left]);
// [begin, left-1] left [left+1, end]
if(left-1>begin)
QuickSort(arr,begin,left-1);
if(left+1<end)
QuickSort(arr,left+1,end);
}
单趟排序目的:找一个key下标的值,单趟排序结束后key的左边比它小,右边比它大,这样为一次单趟排序。
这里暂时先不使用三数取中的优化,先找一个key值(一般为最左边或最右边),这里默认为left下标处的值。然后定义left,right。右边找小于key的值,左边找大于key的值,且必须是right先走。(这里有原因,后序解答)。当右边找到小,左边找到大,交换左右下标的值,使得大值去右边,小值去左边。当left和right相遇之后循环退出,再交换key下标的值,和相遇处的值(这里取left和right都可以) 至此,单趟排序结束。
细节1:右边一定要找小于key的值,左边一定要找大于key的值,也就是while循环中必须为arr[right]>=arr[key]。不可以为arr[right] > arr[key] 为了防止如下情况:5 5 1 2 3 4 5 这样就会陷入死循环。
细节2:每个循环都要写left<right这个条件。而不管在任何一个循环处达到了left=right 都可以正常停下,while里的swap也只是没有意义的交换,然后退出大的while。
为什么key在左边必须右边先走?举例所有情况,你会发现,只有右边先走才可以达成相遇处的值<= arr[key] 情况1:右边去遇到左边:此时left或者为初始的key下标。或者是上一次left right交换之后的left。初始的key说明key右边的所有值都大于等于key下标的值,相遇之后相当于自己交换。而上一次交换之后的left一定小于key。 所以综上情况1的两种可能,都可以达到相遇处的值小于等于arr[key](事实上可能性1的概率很小) 这样交换之后才可以保证key左边的值小于它,右边的值大于他。