忙完論文的事,筆者終於有時間繼續學習算法了。以前一直覺得快速排序(Quick Sort)很神祕,今天筆者就學習一下《算法導論》關於快速排序的部分內容。
快速排序是一種最快情況時間複雜度爲O(n^2)的排序算法,但是其平均性能較好,且爲原址排序算法,因此像Java的Arrays類中的靜態函數sort
就是使用快速排序實現的。與歸併排序算法一樣,快速排序也是使用了分治思想。對一個數組A[p…r]快速排序算法通常分爲三步:
- 數組A[p…r]被劃分爲兩個(可能爲空)的子數組A[p…q-1]和A[q+1…r],使得A[p…q-1]中的每一個元素都小於等於A[q],而A[q]也小於等於A[q+1…r]中的每一個元素。其中,計算小標q是快速排序算法中的一個重要步驟。
- 通過遞歸調用快速排序,對子數組A[p…q-1]和A[q+1…r]進行排序。
- 因爲子數組均爲原址排序,因此不需要進行合併操作。
演示代碼如下:
package org.vimist.pro.Algorithm.Sort;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Random;
/**
* An illustration of {@code QuickSort}.
*
* @author Mr.K
*/
public class QuickSort {
public static void main(String[] args) {
int N = 20;
Random random = new Random();
int[] arr = new int[N];
for (int i = 0; i < arr.length; i++) {
arr[i] = random.nextInt(3 * N);
}
System.out.println("待排序數組: " + Arrays.toString(arr));
Quick_Sort(arr, 0, arr.length - 1);
System.out.println("已排序數組: " + Arrays.toString(arr));
}
/**
* Accepts an array, start index and end index and sorts the specified array
* by using {@code QuickSort}, which is stable and this sort needs no spare
* space.
* <ul>
* <li>If index start is less than index end, the find the index i of primary
* element in the range [start, end], and invokes this method itself twice.
* In the first time, the 3-rd parameter changes to i - 1 and the 2-nd
* parameter changes to i + 1 in the second time when invoking itself.</li>
* </ul>
* Be careful, the complexity of time for this method may be O(n ^ 2) in the worst
* cases, but in general, it comes to O(n * log(n)). In other word, this method
* has good performance in general cases.
*
* @param arr specified array to be sorted
* @param start start index of the array
* @param end end index of the array
*/
private static void Quick_Sort(@NotNull int[] arr, @NotNull int start, @NotNull int end) {
if (start < end) {
int mid = _partition(arr, start, end);
Quick_Sort(arr, start, mid - 1);
Quick_Sort(arr, mid + 1, end);
}
}
/**
* Core of {@code QuickSort}. This method is going to divide the specified
* array into two sub-arrays, which may be empty. There exists a <em>For-
* Loop</em>, which start from the index start(inclusive) and ends at the
* index end(exclusive). In each iteration, if current element is less than
* or equal to the last element in the specified array, then changes both
* elements, one of which uses index <s>i</s>, which starts from start - 1.
* After the end of <em>For-Loop</em>, exchanges both numbers, one of which
* is represented by i + 1, and the other one is end. At last, return i + 1,
* such that the elements in the range of [start, i] are less than or equal to
* element indexed by i + 1, and the elements is in the range of [i + 2, end]
* are greater than element indexed by i + 1.
*
* @param arr specified array
* @param start start index of array
* @param end end index of array
* @return an index which separate specified array into two sub-arrays, where
* all elements in the former array are less than or equals to the primary
* element and all elements in the later array are greater than the primary
* element.
*/
private static int _partition(@NotNull int[] arr, @NotNull int start, @NotNull int end) {
int x = arr[end], i = start - 1;
for (int j = start; j < end; j++) {
if (arr[j] <= x) {
_exchange(arr, ++i, j);
}
}
_exchange(arr, i + 1, end);
return i + 1;
}
/**
* Exchanges two numbers via specified indexes in the specified array.
*
* @param arr specified array
* @param i one of the specified index
* @param j another one of the specified index
*/
private static void _exchange(@NotNull int[] arr, @NotNull int i, @NotNull int j) {
int num = arr[i] ^ arr[j];
arr[i] = num ^ arr[i];
arr[j] = num ^ arr[j];
}
}
運行結果如下:
待排序數組: [25, 9, 45, 9, 29, 15, 0, 7, 28, 10, 20, 31, 21, 6, 53, 30, 44, 26, 34, 33]
已排序數組: [0, 6, 7, 9, 9, 10, 15, 20, 21, 25, 26, 28, 29, 30, 31, 33, 34, 44, 45, 53]