快速排序
快速排序
快速排序是一种分治的排序算法。它将一个数组分成两个子数组,将两部分独立地排序。快速排序和归并排序是互补的:归并排序是将数组分成两个子数组分别排序,并将有序的子数组归并以将整个数组排序,而快速排序将数组排序的方式则是当两个子数组都有序时整个数组也就自然有序了,归并排序的地柜调用发生在处理整个数组之前,而快速排序的地柜调用则发生在处理数组之后。
快速排序的基本思想是:
- 先从数列中选出一个数作为基准数
- 分区过程中,将比基准数小的数放在基准数的左边,比基准数大的数放在基准数的右边
- 再对左右两边分区重复第二个操作,直到各区间只剩一个数
如表1-1,为7个元素,分别是:1 10 -5 9 8 7 3 进行快速排序
初始,把1当做基准数 | 1 | 10 | -5 | 9 | 8 | 7 | 3 |
第一次划分,1归位,由于1的左边只有一位,所以直接从1的右区间开始排序,把10当做基准数 | -5 | 1 | 10 | 9 | 8 | 7 | 3 |
第二次划分,10归位,把3当做基准数 | 3 | 9 | 8 | 7 | 10 | ||
第三次划分,3归位,把9当做基准数 | 3 | 9 | 8 | 7 | |||
第四次划分,9归位,把7当做基准数 | 7 | 8 | 9 | ||||
第五次划分,7归位,由于8只剩下一位,所以不进行排序 | 7 | 8 | |||||
最后 | -5 | 1 | 3 | 7 | 8 | 9 | 10 |
代码1-2为快速排序的C语言实现
#include <stdio.h> void sort( int arr[], int left, int right ); void sort( int arr[], int left, int right ) { int i = left, j = right; int tmp = arr[left]; if ( left >= right ) { return; } while ( i < j ) { while ( i<j &&arr[j]>tmp ) { j--; } arr[i] = arr[j]; while ( i < j && arr[i] < tmp ) { i++; } arr[j] = arr[i]; } arr[i] = tmp; sort( arr, left, i - 1 ); sort( arr, i + 1, right ); } void main() { int i = 0; int arr[] = { 1, 10, -5, 9, 8, 7, 3 }; int len = sizeof(arr) / sizeof(arr[0]); int tmp_arr[len]; printf( "待排序数组:" ); for ( i = 0; i < len; i++ ) { printf( "%d ", arr[i] ); } printf( "\n" ); sort( arr, 0, len - 1 ); printf( "排序后数组:" ); for ( i = 0; i < len; i++ ) { printf( "%d ", arr[i] ); } }
代码1-3为快速排序的Java实现
import java.util.Arrays; public class Quick { public static void sort(int arr[], int left, int right) { if (left >= right) { return; } int i = left, j = right; int tmp = arr[i]; while (i < j) { while (i < j && arr[j] > tmp) { j--; } arr[i] = arr[j]; while (i < j && arr[i] < tmp) { i++; } arr[j] = arr[i]; } arr[i] = tmp; sort(arr, left, i - 1); sort(arr, i + 1, right); } public static void main(String[] args) { int arr[] = { 1, 10, -5, 9, 8, 7, 3 }; System.out.println("待排序数组:" + Arrays.toString(arr)); sort(arr, 0, arr.length - 1); System.out.println("排序后数组:" + Arrays.toString(arr)); } }
代码1-4为快速排序的Python实现
# coding:utf-8 def sort(arr, left, right): if left >= right: return i = left j = right tmp = arr[left] while i < j: while i < j and arr[j] > tmp: j -= 1 arr[i] = arr[j] while i < j and arr[i] < tmp: i += 1 arr[j] = arr[i] arr[i] = tmp sort(arr, left, i - 1) sort(arr, i + 1, right) arr = [1, 10, -5, 9, 8, 7, 3] print "待排序数组:", arr sort(arr, 0, len(arr) - 1) print "排序后数组", arr
代码1-5为快速排序的Scala实现
import java.util.Arrays object Quick { def sort(list: List[Int]): List[Int] = { list match { case Nil => Nil case head :: tail => { val (left, right) = tail.partition(_ < head) sort(left) ::: head :: sort(right) } } } def main(args: Array[String]): Unit = { val list = List(1, 10, -5, 9, 8, 7, 3) println("待排序数组:" + Arrays.toString(list.toArray)) println("排序后数组:" + Arrays.toString(sort(list).toArray)) } }
快速排序在最优的情况下 ,每次基准数的划分都很均匀,即左右两个区间大小相差不大,如果要对N个元素进行排序,则递归树的深度h<=1+logN,仅需要递归logN次。假设需要的时间为T(n),第一次划分需要对整个数组扫描一遍,做n次比较,然后基于基准数将整个数组一分为二,那么左右两个区间还需要T(n/2)的时间,可以得到公式:
T(n)=2*T(n/2)+n
= 2(2T(n/4)+n/2)+n=4T(n/4)+2n
=4(2T(n/8)+n/4)+2n=8T(n/8)+3n
=……
=nT(1)+nlogn
所以快速排序最优情况下的时间复杂度为O(nlogn)
快速排序在最差情况下,假设数组的顺序是正序或者逆序时,每一次划分得到的两个子数组,一个为空,一个只比上一次划分得到的子数组少一个元素,需要执行n-1次递归调用,且第i次划分需要经过n-i次比较才可以为基准数找到最合适的位置,因此,比较的次数为:
(n-1)+(n-2)+……+2+1=n(n-1)/2
所以快速排序最差情况下的时间复杂度为O(n2)