关于快速排序
2017-03-05 20:31
169 查看
快速排序是对冒泡排序的一种改进。关于冒泡排序请参考上篇。快速排序的排序效率高,实用性强;它是原址排序,但不稳定;就平均性能而言,它是最好的排序方法。快速排序是基于分治思想的一种算法,先对原数组进行划分,而后对划分后的两个数组用同样的排序操作。分治法有分割、解决,处理三步,其中最核心部分为第一步:划分parition。
划分:首先选择一个基准pivot,将数组A[1...n]划分为两个数组A[1...k-1]和A[k+1...n] , 前者中的值都 小于pivot,后者中的值都大于pivot,A[k]=pivot,返回k值(注意:此时pivot即A[k]已经在最终的位置,因此它不在这两个数组中的任何一个之中);
解决:对上步的两个数组分别进行快速排序,递归的进行便得到有序数组。合并:由于它是原址排序,因此无需进行合并。关于划分:(1)假设数组第一个值为pivot,并将其存储在key中。设两个变量i、j。首先j先从后向前遍历,如果碰到值大于等于pivot,什么都不做并将j继续向前移动,否则将A[j]的值与A[i]的值交换,j暂停;i从前向后遍历,若碰到的值小于pivot,则继续向后移动不做任何操作,一直碰到一个元素大于pivot,再将此时的A[i]与A[j]值交换,i暂停。再将j由前值继续向前移动,所做与上述操作类似,直到i与j相遇为止,便得到一个划分。其代码如下int parition(int a[],int low,int high){ int key = a[low]; //int key = a[0]; while(low<high){//结束条件:两者相遇之前都是可以的 while(low<high && a[high]>=key)//key保存的是第一个值,先从最后面的开始 high--; if(a[high]<key) swap(&a[low],&a[high]); while(low<high && a[low]<=key)//再从前面开始 low++; if(a[low]>key) swap(&a[low],&a[high]); }//while return low;}(2)或者不将A[i]和A[j]的值进行交换操作,而是直接将一个值赋给后面的值,类似挖坑填坑的方式。首先将第一个值放到key,挖一个坑;再将A[j]放到上一个坑中,那么A[j]处又多了一个坑,如此进行下去,只需最后将key值放到A[i]中(最后i与j值是相同的)。代码如下:int parition2(int a[],int low,int high){ int key = a[low]; while(low < high){ while(low<high && a[high]>=key) high--; if(a[high] < key) a[low] = a[high]; while(low<high && a[low]<=key) low++; if(a[low] > key) a[high] = a[low]; }//while a[low] = key;//挖坑的话,需要处理最后一次 return low;}(3)《算法导论》中有一种与之不同的划分方法,它以最后一个值作为pivot,赋值到key,low与i之间存放的是小于等于pivot的值,i+1与j-1之间存放的是大于pivot的值,j之后的值还未判断。初始化i为low-1处的值,j为low处的值。(初始化书上说是为了满足循环不变量就是low、i、j之间的值)判断A[j]与key的值,若A[j]<key,先把i值加一,再将A[j]与A[]交换,并将j的值加1;若A[j]>key,j加一;当j到达数组最后结束。若按照《算法导论》上代码实现如下:int parition3(int a[],int low,int high){ int i = low - 1; for(int j=low;j<high;j++){ if(a[j]<a[high]) swap(&a[++i],&a[j]); }//for swap(&a[++i],&a[high]); return i;}此处i的取值会超出数组的范围,所以更喜欢下面这种方式,经试验两种都是可以,只是把i的取值 变了一下:int parition4(int a[],int low,int high){ int i= low; int key = a[high]; for(int j=low;j<high;j++){ if(a[j] < key) swap(&a[i++],&a[j]); }//for swap(&a[i],&a[high]); return i;}java实现代码如下:package com.hanlin;
public class QuickSort { static int partition(int[] A,int p,int q){ int x = A[p]; int i = p; for(int j=p+1;j<=q;j++){ if(A[j]<=x){ i++; int temp = A[j]; A[j] = A[i]; A[i] = temp; }//if }//for int temp = A[p]; A[p] = A[i]; A[i] = temp; return i;}
public static void quicksort(int[] A,int p,int q){ if(p<q){ int r = partition(A,p,q); quicksort(A,p,r-1); quicksort(A,r+1,q); }}public static void main(String[] args) {int[] A = {4,65,34,87,1,3,0,85};int a = partition(A,0,7);System.out.println("a = "+a);for(int i=0;i<A.length;i++){System.out.print(A[i]+" ");}System.out.println();quicksort(A,0,A.length-1);for(int i=0;i<A.length;i++){System.out.print(A[i]+" ");}
}
}总结:上述有两种快速排序的算法,每种有一些细节方面的不同,但不碍大局。第一种是《王道》等教科书上经常采用的严老师的一种方法,但是出现多个while感觉非常不爽!而《算法导论》中的这种方法真是完美、简单,只需要一个for循环即可。虽然前者更容易理解,但我更倾向于后者。目前我所写的只是关于算法的实现,算法的时间空间复杂度等部分先不描述。修改的时候,发现快排的优化已经忘了,等以后再补充一下。
参考:
1:王道--《数据结构》2、《算法导论》
划分:首先选择一个基准pivot,将数组A[1...n]划分为两个数组A[1...k-1]和A[k+1...n] , 前者中的值都 小于pivot,后者中的值都大于pivot,A[k]=pivot,返回k值(注意:此时pivot即A[k]已经在最终的位置,因此它不在这两个数组中的任何一个之中);
解决:对上步的两个数组分别进行快速排序,递归的进行便得到有序数组。合并:由于它是原址排序,因此无需进行合并。关于划分:(1)假设数组第一个值为pivot,并将其存储在key中。设两个变量i、j。首先j先从后向前遍历,如果碰到值大于等于pivot,什么都不做并将j继续向前移动,否则将A[j]的值与A[i]的值交换,j暂停;i从前向后遍历,若碰到的值小于pivot,则继续向后移动不做任何操作,一直碰到一个元素大于pivot,再将此时的A[i]与A[j]值交换,i暂停。再将j由前值继续向前移动,所做与上述操作类似,直到i与j相遇为止,便得到一个划分。其代码如下int parition(int a[],int low,int high){ int key = a[low]; //int key = a[0]; while(low<high){//结束条件:两者相遇之前都是可以的 while(low<high && a[high]>=key)//key保存的是第一个值,先从最后面的开始 high--; if(a[high]<key) swap(&a[low],&a[high]); while(low<high && a[low]<=key)//再从前面开始 low++; if(a[low]>key) swap(&a[low],&a[high]); }//while return low;}(2)或者不将A[i]和A[j]的值进行交换操作,而是直接将一个值赋给后面的值,类似挖坑填坑的方式。首先将第一个值放到key,挖一个坑;再将A[j]放到上一个坑中,那么A[j]处又多了一个坑,如此进行下去,只需最后将key值放到A[i]中(最后i与j值是相同的)。代码如下:int parition2(int a[],int low,int high){ int key = a[low]; while(low < high){ while(low<high && a[high]>=key) high--; if(a[high] < key) a[low] = a[high]; while(low<high && a[low]<=key) low++; if(a[low] > key) a[high] = a[low]; }//while a[low] = key;//挖坑的话,需要处理最后一次 return low;}(3)《算法导论》中有一种与之不同的划分方法,它以最后一个值作为pivot,赋值到key,low与i之间存放的是小于等于pivot的值,i+1与j-1之间存放的是大于pivot的值,j之后的值还未判断。初始化i为low-1处的值,j为low处的值。(初始化书上说是为了满足循环不变量就是low、i、j之间的值)判断A[j]与key的值,若A[j]<key,先把i值加一,再将A[j]与A[]交换,并将j的值加1;若A[j]>key,j加一;当j到达数组最后结束。若按照《算法导论》上代码实现如下:int parition3(int a[],int low,int high){ int i = low - 1; for(int j=low;j<high;j++){ if(a[j]<a[high]) swap(&a[++i],&a[j]); }//for swap(&a[++i],&a[high]); return i;}此处i的取值会超出数组的范围,所以更喜欢下面这种方式,经试验两种都是可以,只是把i的取值 变了一下:int parition4(int a[],int low,int high){ int i= low; int key = a[high]; for(int j=low;j<high;j++){ if(a[j] < key) swap(&a[i++],&a[j]); }//for swap(&a[i],&a[high]); return i;}java实现代码如下:package com.hanlin;
public class QuickSort { static int partition(int[] A,int p,int q){ int x = A[p]; int i = p; for(int j=p+1;j<=q;j++){ if(A[j]<=x){ i++; int temp = A[j]; A[j] = A[i]; A[i] = temp; }//if }//for int temp = A[p]; A[p] = A[i]; A[i] = temp; return i;}
public static void quicksort(int[] A,int p,int q){ if(p<q){ int r = partition(A,p,q); quicksort(A,p,r-1); quicksort(A,r+1,q); }}public static void main(String[] args) {int[] A = {4,65,34,87,1,3,0,85};int a = partition(A,0,7);System.out.println("a = "+a);for(int i=0;i<A.length;i++){System.out.print(A[i]+" ");}System.out.println();quicksort(A,0,A.length-1);for(int i=0;i<A.length;i++){System.out.print(A[i]+" ");}
}
}总结:上述有两种快速排序的算法,每种有一些细节方面的不同,但不碍大局。第一种是《王道》等教科书上经常采用的严老师的一种方法,但是出现多个while感觉非常不爽!而《算法导论》中的这种方法真是完美、简单,只需要一个for循环即可。虽然前者更容易理解,但我更倾向于后者。目前我所写的只是关于算法的实现,算法的时间空间复杂度等部分先不描述。修改的时候,发现快排的优化已经忘了,等以后再补充一下。
参考:
1:王道--《数据结构》2、《算法导论》
相关文章推荐
- 关于排序(快速排序)
- 关于快速排序和插入排序最坏时间复杂度为O(nlogn)的算法
- 关于快速排序的Java代码实现
- 关于堆排序、归并排序、快速排序的比较,到底谁快
- 关于快速排序
- \t\t转载:关于快速排序的优化
- 为什么你写不好一个快速排序?关于
- 关于快速排序的学习
- 算法学习---关于快速排序,数据呈现基本有序和完全无序时候导致的时间复杂度增n^2的解决方案
- 关于快速排序 quick sort
- 关于快速排序的见解(分治法)
- 关于快速排序——Java实现
- 说一说程序员“举一反三”的能力(关于快速排序及其变形)
- 关于快速排序的错误问题
- 关于快速排序的c++算法
- 关于快速排序的优化
- 关于白话排序之快速排序以及维基百科的希尔排序
- 关于快速排序
- 关于快速排序的实现方式
- 算法导论——关于快速排序的实现(c语言实现)