快速排序(递归版)

来源:互联网 发布:免费标签打印软件 编辑:程序博客网 时间:2024/06/07 10:42

快速排序是最经典的排序算法,它用途广泛,效率很高,也经常被拿来检验coder的基本功底。

此篇作为快排的学习笔记。


快排的基本思想

快速排序是典型的分治算法,理解了快排的思想,很容易写出递归版的代码。

快排分以下三个步骤.

  1. Choose a pivot value. 选择一个元素作为(pivot),这里我一般选择数组的中间值作为轴。
  2. Partition.将当前数组划分。划分遵循这样的规则,所有小于轴的元素,放在轴的左边,大于轴的元素,放在轴的右边,等于轴的元素放在哪边都可以。这样相当于将一个数组划分成了两个部分。
  3. Sort both parts。对划分后的数组不断重复1,2过程,直到数组有序。

这样为什么可以得到有序的数组呢?

每次划分操作都将数组划分为两部分。
左半部分的任何元素都将<=右半部分的任何元素。
所以递归的重复以上过程,最终得到的数组一定是有序的。

下面给出这个递归过程的伪代码

QuickSort(array,low,high)    if(low < high) : //满足递归条件        pivotIndex = Partition(array,low,high)        QuickSort(array,low,pivotIndex - 1)        QuickSort(array,pivotIndex + 1,high);

这个思想十分简单明了,难点在于如何编写Partiton()函数。
实际上,也并不难理解

Partition的编写与过程分析

我们重述一下Partation函数的作用。

划分:在数组中挑选一个作为划分标准,所有小于轴的元素放到轴的左边,大于轴的元素放到轴的右边。然后Partation函数会返回这个最终的位置(同时也是轴在最终有序数列中的位置)

WiKi的伪代码:

partition(A, lo, hi)     pivotIndex := choosePivot(A, lo, hi)     pivotValue := A[pivotIndex]     // put the chosen pivot at A[hi]     swap A[pivotIndex] and A[hi]      storeIndex := lo     // Compare remaining array elements against pivotValue = A[hi]     for currIndex from lo to hi−1, inclusive         if A[currIndex] < pivotValue             swap A[currIndex] and A[storeIndex]             storeIndex := storeIndex + 1     swap A[storeIndex] and A[hi]  // Move pivot to its final place     return storeIndex

分析 : 上述伪代码中有两个重要的变量。

  1. storeIndex : 这个变量指示的是pivot经过一次划分后的最终坐标位置,初始化为low。
  2. currIndex : 顾名思义指示的是当前的循环位置

Q:为什么要将pivot和A[high]预先交换?
A : 首先pivotIndex的选取是没有什么规定的,所以这里以一个函数choosePivot代替。为了实现算法,我们必须将除了pivot之外的所有元素与pivot比较,如果pivot是A[high]那么循环写起来会很方便,否则就将pivot与A[high]交换。

Q:for循环的作用是什么?
A:循环体内依次遍历除pivot的所有元素,将所有小于pivot的元素放到pivot最终位置(storeIndex)的左边。(详细用图示解释)

Q:为何循环完毕后还要交换一次?
A:循环完毕只是将所有小于pivot的元素放到了storeIndex(pivot最终位置)的左边,而pivot还是在A[high]位置上,因此要交换。
值得注意的是,这里不可以写成swap(A[storeIndex],pivot),虽然还是把pivot放到了最终位置上,但是这样做并没有改变A[high]的值(A[high]还是pivot),所以最后一步是必不可少的。

下面给出一个图示详细说明一下 :
初始化

青绿色表示所有小于pivot的元素

(3 < pivot,因为curr和store开始指向同一元素,3和3自身交换,s++,c++)
这里写图片描述

(7 > pivot,c++)
这里写图片描述

(4 < pivot,4 和 7 交换,c++,s++)
这里写图片描述

(2 < pivot,2 和 8 交换,c++, s++)
这里写图片描述

(1 < pivot,1 和 7 交换,c++,s++)
这里写图片描述

(循环结束)
这里写图片描述

(最后,swap A[storeIndex] and A[hi])
这里写图片描述

C++代码

int QuickSort(int array[],int low,int high){    if(low < high){        int pivotIndex = Partition(array,low,high);//如果可以划分        QuickSort(array,low,pivotIndex - 1);        QuickSort(array,pivotIndex + 1,high);    }}
int Partition(int array[],int low,int high){    int pivotIndex = (low + high) / 2;    int pivot = array[pivotIndex];    int storeIndex = low;//pivot所在的最终位置    //put the chosen pivot at array[high]    swap(array[pivotIndex],array[high]);    //put the chosen pivot at array[high]    for(int currIndex = low ; currIndex < high ; ++currIndex){        if(array[currIndex] <= pivot){//也可以写<=            swap(array[currIndex],array[storeIndex]);            storeIndex++;        }    }    swap(array[storeIndex],array[high]);//move pivot to its final position    //you can never write swap(array[storeIndex],pivot]    //cause if you use this form,you can not change the value of array[high]    //that's wrong    return storeIndex;}
1 0
原创粉丝点击