快速排序(Quicksort)是对冒泡排序算法的一种改进。
快速排序由C. A. R. Hoare在1960年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
1. 算法原理
1、首先设定一个分界值,通过该分界值将数组分成左右两部分。
2、将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
3、然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
4、重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
2. 算法步骤
设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它左边,所有比它大的数都放到它右边,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快速排序的算法是:
1、设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2、以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3、从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]的值交换;
4、从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]的值交换;
5、重复第3、4步,直到i==j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。
3. 动图演示
4. 时间复杂度
快速排序的一次划分算法从两头交替搜索,直到low和hight重合,因此其时间复杂度是O(n);而整个快速排序算法的时间复杂度与划分的趟数有关。
理想的情况是,每次划分所选择的中间数恰好将当前序列几乎等分,经过log2n趟划分,便可得到长度为1的子表。这样,整个算法的时间复杂度为O(nlog2n)。
最坏的情况是,每次所选的中间数是当前序列中的最大或最小元素,这使得每次划分所得的子表中一个为空表,另一子表的长度为原表的长度-1。这样,长度为n的数据表的快速排序需要经过n趟划分,使得整个排序算法的时间复杂度为O(n2)。
为改善最坏情况下的时间性能,可采用其他方法选取中间数。通常采用“三者值取中”方法,即比较H->r[low].key、H->r[high].key与H->r[(low+high)/2].key,取三者中关键字为中值的元素为中间数。
可以证明,快速排序的平均时间复杂度也是O(nlog2n)。因此,该排序方法被认为是目前最好的一种内部排序方法。
从空间性能上看,尽管快速排序只需要一个元素的辅助空间,但快速排序需要一个栈空间来实现递归。最好的情况下,即快速排序的每一趟排序都将元素序列均匀地分割成长度相近的两个子表,所需栈的最大深度为log2(n+1);但最坏的情况下,栈的最大深度为n。这样,快速排序的空间复杂度为O(log2n)。
5. 算法实现
快速排序C语言
void sort(int *a, int left, int right) { if(left >= right)/*如果左边索引大于或者等于右边的索引就代表已经整理完成一个组了*/ { return ; } int i = left; int j = right; int key = a[left]; while(i < j) /*控制在当组内寻找一遍*/ { while(i < j && key <= a[j]) /*而寻找结束的条件就是,1,找到一个小于或者大于key的数(大于或小于取决于你想升 序还是降序)2,没有符合条件1的,并且i与j的大小没有反转*/ { j--;/*向前寻找*/ } swap(a,i,j); /*交换a[i]和a[j]的数值*/ /*找到一个这样的数后就把它赋给前面的被拿走的i的值(如果第一次循环且key是 a[left],那么就是给key)*/ while(i < j && key >= a[i]) /*这是i在当组内向前寻找,同上,不过注意与key的大小关系停止循环和上面相反, 因为排序思想是把数往两边扔,所以左右两边的数大小与key的关系相反*/ { i++; } swap(a,i,j); } a[i] = key;/*当在当组内找完一遍以后就把中间数key回归*/ sort(a, left, i - 1);/*最后用同样的方式对分出来的左边的小组进行同上的做法*/ sort(a, i + 1, right);/*用同样的方式对分出来的右边的小组进行同上的做法*/ /*当然最后可能会出现很多分左右,直到每一组的i = j 为止*/ }
快速排序C++
#include <iostream> using namespace std; void Qsort(int arr[], int low, int high){ if (high <= low) return; int i = low; int j = high + 1; int key = arr[low]; while (true) { /*从左向右找比key大的值*/ while (arr[++i] < key) { if (i == high){ break; } } /*从右向左找比key小的值*/ while (arr[--j] > key) { if (j == low){ break; } } if (i >= j) break; /*交换i,j对应的值*/ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } /*中枢值与j对应值交换*/ int temp = arr[low]; arr[low] = arr[j]; arr[j] = temp; Qsort(arr, low, j - 1); Qsort(arr, j + 1, high); } int main() { int a[] = {57, 68, 59, 52, 72, 28, 96, 33, 24}; Qsort(a, 0, sizeof(a) / sizeof(a[0]) - 1);/*这里原文第三个参数要减1否则内存越界*/ for(int i = 0; i < sizeof(a) / sizeof(a[0]); i++) { cout << a[i] << " "; } return 0; }/*参考数据结构p274(清华大学出版社,严蔚敏)*/
快速排序PHP
<?php $arr = array(25,133,452,364,5876,293,607,365,8745,534,18,33); function quick_sort($arr) { // 判断是否需要继续 if (count($arr) <= 1) { return $arr; } $middle = $arr[0]; // 中间值 $left = array(); // 小于中间值 $right = array();// 大于中间值 // 循环比较 for ($i=1; $i < count($arr); $i++) { if ($middle < $arr[$i]) { // 大于中间值 $right[] = $arr[$i]; } else { // 小于中间值 $left[] = $arr[$i]; } } // 递归排序两边 $left = quick_sort($left); $right = quick_sort($right); // 合并排序后的数据,别忘了合并中间值 return array_merge($left, array($middle), $right); } var_dump($arr); var_dump(quick_sort($arr));
快速排序JAVA
public static int[] qsort(int arr[],int start,int end) { int pivot = arr[start]; int i = start; int j = end; while (i<j) { while ((i<j)&&(arr[j]>pivot)) { j--; } while ((i<j)&&(arr[i]<pivot)) { i++; } if ((arr[i]==arr[j])&&(i<j)) { i++; } else { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } if (i-1>start) arr=qsort(arr,start,i-1); if (j+1<end) arr=qsort(arr,j+1,end); return (arr); } public static void main(String[] args) { int arr[] = new int[]{3,3,3,7,9,122344,4656,34,34,4656,5,6,7,8,9,343,57765,23,12321}; int len = arr.length-1; arr=qsort(arr,0,len); for (int i:arr) { System.out.print(i+" "); } } /*//////////////////////////方式二////////////////////////////////*/ 更高效点的代码:(TextendsComparable和SortUtil都是自己封装的类,里面重写和实现了compareTo和swap方法) public <TextendsComparable<?superT>> T[] quickSort(T[] targetArr,int start,int end) { inti=start+1,j=end; T key=targetArr[start]; SortUtil<T> sUtil=new SortUtil<T>(); if(start==end)return(targetArr); /*从i++和j--两个方向搜索不满足条件的值并交换 * *条件为:i++方向小于key,j--方向大于key */ while(true) { while(targetArr[j].compareTo(key)>0)j--; while(targetArr[i].compareTo(key)<0&&i<j)i++; if(i>=j)break; sUtil.swap(targetArr,i,j); if(targetArr[i]==key) { j--; }else{ i++; } } /*关键数据放到‘中间’*/ sUtil.swap(targetArr,start,j); if(start<i-1) { this.quickSort(targetArr,start,i-1); } if(j+1<end) { this.quickSort(targetArr,j+1,end); } returntargetArr; } /*//////////////方式三:减少交换次数,提高效率/////////////////////*/ private<TextendsComparable<?superT>> voidquickSort(T[]targetArr,intstart,intend) { inti=start,j=end; Tkey=targetArr[start]; while(i<j) { /*按j--方向遍历目标数组,直到比key小的值为止*/ while(j>i&&targetArr[j].compareTo(key)>=0) { j--; } if(i<j) { /*targetArr[i]已经保存在key中,可将后面的数填入*/ targetArr[i]=targetArr[j]; i++; } /*按i++方向遍历目标数组,直到比key大的值为止*/ while(i<j&&targetArr[i].compareTo(key)<=0) /*此处一定要小于等于零,假设数组之内有一亿个1,0交替出现的话,而key的值又恰巧是1的话,那么这个小于等于的作用就会使下面的if语句少执行一亿次。*/ { i++; } if(i<j) { /*targetArr[j]已保存在targetArr[i]中,可将前面的值填入*/ targetArr[j]=targetArr[i]; j--; } } /*此时i==j*/ targetArr[i]=key;//应加判断 /*递归调用,把key前面的完成排序*/ this.quickSort(targetArr,start,i-1); /*递归调用,把key后面的完成排序*/ this.quickSort(targetArr,j+1,end); //两个递归应加判断 }
快速排序JavaScript
const quickSort = (array) => { const sort = (arr, left = 0, right = arr.length - 1) => { if (left >= right) {//如果左边的索引大于等于右边的索引说明整理完毕 return } let i = left let j = right const baseVal = arr[j] // 取无序数组最后一个数为基准值 while (i < j) {//把所有比基准值小的数放在左边大的数放在右边 while (i < j && arr[i] <= baseVal) { //找到一个比基准值大的数交换 i++ } arr[j] = arr[i] // 将较大的值放在右边如果没有比基准值大的数就是将自己赋值给自己(i 等于 j) while (j > i && arr[j] >= baseVal) { //找到一个比基准值小的数交换 j-- } arr[i] = arr[j] // 将较小的值放在左边如果没有找到比基准值小的数就是将自己赋值给自己(i 等于 j) } arr[j] = baseVal // 将基准值放至中央位置完成一次循环(这时候 j 等于 i ) sort(arr, left, j-1) // 将左边的无序数组重复上面的操作 sort(arr, j+1, right) // 将右边的无序数组重复上面的操作 } const newArr = array.concat() // 为了保证这个函数是纯函数拷贝一次数组 sort(newArr) return newArr }
快速排序C#语言
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace test { class QuickSort { static void Main(string[] args) { int[] array = { 49, 38, 65, 97, 76, 13, 27 }; sort(array, 0, array.Length - 1); Console.ReadLine(); } /**一次排序单元,完成此方法,key左边都比key小,key右边都比key大。 **@param array排序数组 **@param low排序起始位置 **@param high排序结束位置 **@return单元排序后的数组 */ private static int sortUnit(int[] array, int low, int high) { int key = array[low]; while (low < high) { /*从后向前搜索比key小的值*/ while (array[high] >= key && high > low) --high; /*比key小的放左边*/ array[low] = array[high]; /*从前向后搜索比key大的值,比key大的放右边*/ while (array[low] <= key && high > low) ++low; /*比key大的放右边*/ array[high] = array[low]; } /*左边都比key小,右边都比key大。//将key放在游标当前位置。//此时low等于high */ array[low] = key; foreach (int i in array) { Console.Write("{0} ", i); } Console.WriteLine(); return high; } /**快速排序 *@paramarry *@return */ public static void sort(int[] array, int low, int high) { if (low >= high) return; /*完成一次单元排序*/ int index = sortUnit(array, low, high); /*对左边单元进行排序*/ sort(array, low, index - 1); /*对右边单元进行排序*/ sort(array, index + 1, high); } } }
快速排序Objective-C
+ (void)quickSort:(NSMutableArray *)m low:(int)low high:(int)high{ if (low >= high) { return; } int i = low; int j = high; id key = m[i]; while (i<j) { while (i<j && [m[j] intValue] >= [key intValue]) { j--; } if (i == j) { // 当key是目前最小的数时,会出现i=j的情况, break; } m[i++] = m[j]; // i++ 会减少一次m[i]和key的比较 while (i < j && [m[i] intValue] <= [key intValue]) { i++; } if (i == j) { // 当key是目前最大的数时(m[j]的前面),会出现i=j的情况 break; } m[j--] = m[i]; //j-- 会减少一次m[j]和key的比较 } m[i] = key; [self quickSort: m low: low high: i-1]; [self quickSort: m low: i+1 high: high]; // NSLog(@"快速排序 %@",m); }
快速排序Python3
def quick_sort(data): """快速排序""" if len(data) >= 2: # 递归入口及出口 mid = data[len(data)//2] # 选取基准值,也可以选取第一个或最后一个元素 left, right = [], [] # 定义基准值左右两侧的列表 data.remove(mid) # 从原始数组中移除基准值 for num in data: if num >= mid: right.append(num) else: left.append(num) return quick_sort(left) + [mid] + quick_sort(right) else: return data # 示例: array = [2,3,5,7,1,4,6,15,5,2,7,9,10,15,9,17,12] print(quick_sort(array)) # 输出为[1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 9, 9, 10, 12, 15, 15, 17]
快速排序Swift
func quickSort(a: inout [Int], low: Int, high: Int) { if low >= high { // 递归结束条件 return } var i = low var j = high let key = a[i] while i < j { // 从右边开始比较,比key大的数位置不变 while i < j && a[j] >= key { j -= 1 } // 只要出现一个比key小的数,将这个数放入左边i的位置 a[i] = a[j] // 从左边开始比较,比key小的数位置不变 while i < j && a[i] <= key { i += 1 } // 只要出现一个比key大的数,将这个数放入右边j的位置 a[j] = a[i] } a[i] = key // 将key放入i的位置,则左侧数都比key小,右侧数都比key大 quickSort(a: &a, low: low, high: i - 1) // 左递归 quickSort(a: &a, low: i + 1, high: high) // 右递归 } // 示例 var m = [2,3,5,7,1,4,6,15,5,2,7,9,10,15,9,17,12] quickSort(a: &m, low: 0, high: m.count - 1) print(m) // 结果:[1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 9, 9, 10, 12, 15, 15, 17]
快速排序RUBY
def quick_sort(a) (x=a.pop) ? quick_sort(a.select { |i| i <= x }) + [x] + quick_sort(a.select { |i| i > x }) : [] end
快速排序Erlang
q_sort([])-> []; q_sort([H|R])-> q_sort([X||X<-R,X<H])++[H]++ q_sort([X||X<-R,X>=H]).
快速排序Go语言
// 第一种写法 func quickSort(values []int, left, right int) { temp := values[left] p := left i, j := left, right for i <= j { for j >= p && values[j] >= temp { j-- } if j >= p { values[p] = values[j] p = j } for i <= p && values[i] <= temp { i++ } if i <= p { values[p] = values[i] p = i } } values[p] = temp if p-left > 1 { quickSort(values, left, p-1) } if right-p > 1 { quickSort(values, p+1, right) } } func QuickSort(values []int) { if len(values) <= 1 { return } quickSort(values, 0, len(values)-1) } // 第二种写法 func Quick2Sort(values []int) { if len(values) <= 1 { return } mid, i := values[0], 1 head, tail := 0, len(values)-1 for head < tail { fmt.Println(values) if values[i] > mid { values[i], values[tail] = values[tail], values[i] tail-- } else { values[i], values[head] = values[head], values[i] head++ i++ } } values[head] = mid Quick2Sort(values[:head]) Quick2Sort(values[head+1:]) } // 第三种写法 func Quick3Sort(a []int,left int, right int) { if left >= right { return } explodeIndex := left for i := left + 1; i <= right ; i++ { if a[left] >= a[i]{ //分割位定位++ explodeIndex ++; a[i],a[explodeIndex] = a[explodeIndex],a[i] } } //起始位和分割位 a[left], a[explodeIndex] = a[explodeIndex],a[left] Quick3Sort(a,left,explodeIndex - 1) Quick3Sort(a,explodeIndex + 1,right) }
快速排序PASCAL
这里是完全程序,过程部分为快排 program qsort; var n,p:integer; a:array[0..100000] of integer; procedure qs(l,r:integer);//假设被排序的数组是a,且快排后按升序排列) var i,j,m,t:integer; begin i:=l; j:=r;//(l(left),r(right)表示快排的左右区间) m:=a[(l+r)div2];//注意:本句不能写成:m:=(l+r)div2; repeat while a[i]<m do inc(i); while a[j]>m do dec(j);//若是降序把'<'与‘>'互换; if i<=j then begin t:=a[i]; a[i]:=a[j]; a[j]:=t; inc(i); dec(j); end; until i>j; if l<j then qs(l,j);//递归查找左区间 if i<r then qs(i,r);//递归查找右区间 end; begin readln(n);//有n个数据要处理 for p:=1 to n do read(a[p]);//输入数据 qs(1,n); for p:=1 to n do write(a[p],'');//输出快排后的数据 end. 或者 procedure quickSort(var a:array of integer;l,r:Integer); var i,j,x:integer; begin if l>=r then exit; i:=l; j:=r; x:=a[i]; while i<=j do begin while (i<j)and(a[j]>x) do dec(j); if i<j then begin a[i]:=a[j]; inc(i); end; while (i<j)and(a[i]<x) do inc(i); if i<j then begin a[j]:=a[i]; dec(j); end; a[i]:=x; quicksort(a,l,i-1); quicksort(a,i+1,r); end; end;