快速排序

Quicksort is a fast sorting algorithm, which is used not only for educational purposes, but widely applied in practice. On the average, it has O(n log n) complexity, making quicksort suitable for sorting big data volumes. The idea of the algorithm is quite simple and once you realize it, you can write quicksort as fast as bubble sort

.

快速排序是一种快速的排序算法,它不只能用于教学目的,在实际中也有着广泛的应用。平均来说,其复杂度为O(nlogn),使得快排很适合对大数据量进行排序。该算法的思路非常简单,一旦理解后,你就可以像写冒泡排序一样写出快排算法了。

Algorithm

算法

 

The divide-and-conquer strategy is used in quicksort. Below the recursion step is described:

快排使用”分而治之”的策略。下面是递归步骤


  1. Choose a pivot value.

    We take the value of the middle element as pivot value, but it can be any value, which is in range of sorted values, even if it doesn’t present in the array.


  2. Partition.

    Rearrange elements in such a way, that all elements which are lesser than the pivot go to the left part of the array and all elements greater than the pivot, go to the right part of the array. Values equal to the pivot can stay in any part of the array. Notice, that array may be divided in non-equal parts.


  3. Sort both parts.

    Apply quicksort algorithm recursively to the left and the right parts.

1. 选择关键值。选择一个中间元素作为关键值,不过该关键值不一定必须为待排元素当中的一个,只要为所有待排元素范围内的任何值,

2. 分割。以下列方法将元素重排:所有小于关键值的元素都排至关键值的左侧,大于关键值的元素都排至右侧。等于关键值的元素可左可右。请注意,这样一来,数组分割的两部分不一定是等长的

3. 分别对两部分排序。递归地对上述分割出来的两部分应用排序算法

 

Partition algorithm in detail

具体分割算法

 

There are two indices i and j and at the very beginning of the partition algorithm i points to the first element in the array and j points to the last one. Then algorithm moves i forward, until an element with value greater or equal to the pivot is found. Index j is moved backward, until an element with value lesser or equal to the pivot is found. If i ≤ j then they are swapped and i steps to the next position (i + 1), j steps to the previous one (j – 1). Algorithm stops, when i becomes greater than j.

两个指针ij,一开始,i指向数组第一个元素,j指向最后一个。然后i向前移动,直到遇上大于或等于关键值的元素。j向后移动,直到遇上小于或者等于关键值的元素. 如果i ≤ j ,那么将两个元素交换,i前进一步至(i+1),j继续移动,指向(j-1).i大于j时,算法停止.

 

After partition, all values before i-th element are less or equal than the pivot and all values after j-thelement are greater or equal to the pivot.

分割后,第i号元素之前的元素都小于或等于关键值,第j号之后的元素都大于或等于关键值

Example.

Sort {1, 12, 5, 26, 7, 14, 3, 7, 2} using quicksort.

比如,使用快排对数组{1, 12, 5, 26, 7, 14, 3, 7, 2}进行排序

 

Notice, that we show here only the first recursion step, in order not to make example too long. But, in fact, {1, 2, 5, 7, 3} and {14, 7, 26, 12} are sorted then recursively.

请注意,我们在这只演示了第一次的递归步骤,只是为了不让例子占太长的篇幅。实际上,之后还需要递归对{1, 2, 5, 7, 3} 和{14, 7, 26, 12}进行排序。

Why does it work?

为啥好使?

 

On the partition step algorithm divides the array into two parts and every element a from the left part is less or equal than every element bfrom the right part. Also a and b satisfy a ≤ pivot ≤ binequality. After completion of the recursion calls both of the parts become sorted and, taking into account arguments stated above,the whole array is sorted.

一次分割步骤将数组分成了两个部分,左侧部分中的每一个元素a都小于或等于右侧部分中的任一元素b.并且ab均满足不等式 a ≤ 关键数据≤ b。完成对这两部分的递归调用后,这两部分都是排好序的,考虑前述证明,整个数组都是有序的

Complexity analysis

复杂度分析

 

On the average quicksort has O(n log n) complexity, but strong proof of this fact is not trivial and not presented here. Still, you can find the proof in [1]. In worst case, quicksort runs O(n2) time, but on the most “practical” data it works just fine and outperforms other O(n log n) sorting algorithms.

快排的平均复杂度为 O(n log n),强有力的证明不是那么简单的事,在此就不展示了。不过,可以在参考[1]中找到证明。最坏的情况下,快排运行时间为O(n2),但在大多数的实际情况中,该算法工作表现还不错,优于其它的复杂度为O(n log n)的排序算法。

Code snippets

代码片段

 

Partition algorithm is important per se, therefore it may be carried out as a separate function. The code for C++ contains solid function for quicksort, but Java code contains two separate functions for partition and sort, accordingly.

分割算法本身非常重要,因为可以将其单独写成一个函数。下面的C++代码将所有内容写在一个函数中,而Java代码则包含了两个分开的函数,一个用来分割,一个用来排序。

Java

int partition(int arr[], int left, int right)

{

int i = left, j = right;

int tmp;

int pivot = arr[(left + right) / 2];

 

while (i <= j) {

while (arr[i] < pivot)

i++;

while (arr[j] > pivot)

j–;

if (i <= j) {

tmp = arr[i];

arr[i] = arr[j];

arr[j] = tmp;

i++;

j–;

}

};

 

return i;

}

 

void quickSort(int arr[], int left, int right) {

int index = partition(arr, left, right);

if (left < index – 1)

quickSort(arr, left, index – 1);

if (index < right)

quickSort(arr, index, right);

}

C++

void quickSort(int arr[], int left, int right) {

int i = left, j = right;

int tmp;

int pivot = arr[(left + right) / 2];

 

/* partition */

while (i <= j) {

while (arr[i] < pivot)

i++;

while (arr[j] > pivot)

j–;

if (i <= j) {

tmp = arr[i];

arr[i] = arr[j];

arr[j] = tmp;

i++;

j–;

}

};

 

/* recursion */

if (left < j)

quickSort(arr, left, j);

if (i < right)

quickSort(arr, i, right);

}

 

参考书

  1. Cormen, Leiserson, Rivest. Introduction to algorithms. (Theory)
  2. Aho, Ullman, Hopcroft. Data Structures and Algorithms. (Theory)
  3. Robert Lafore. Data Structures and Algorithms in Java. (Practice)
  4. Mark Allen Weiss. Data Structures and Problem Solving Using C++. (Practice)

 

Advertisements
Post a comment or leave a trackback: Trackback URL.

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s

%d 博主赞过: