快速排序
快速排序算法思想
分治法:比大小,再分区
-
从数组中取出一个数,作为基准数
-
分区:将比这个数大或等于的数全放到它的右边,小于它的数全放到它的左边(为由小到大排序)
-
再对左右区间重复第二步,直到各区间只有一个数。
实现思路:挖坑填数的方法
-
将基准数(可以为第一个数)挖出形成第一个坑
-
由后向前找比他小的数,找到后挖出此书填到前一个坑中。
-
由前向后找比他大或等于的数,找到后也挖出此数填到前一个坑中。
-
再重复执行2,3两步骤。
例如对于实力5391672408进行排序。图片分析过程如下:
就是按照上述挖坑填数的方法进行理解直接选取首个数5作为基准数,取出来并形成坑1,从后向前找比5小的首个数即为0填入坑1同时也形成了坑2,再从前往后找比5大的数即为9填入坑位2,于是形成坑位3,再从后向前找比5小的数即为4填入坑位3自身形成坑位4不断持续...直到再次从前往后找到比5大的数即7填入坑位6自身形成坑位7,但此时再次从后向前找的话,也将到达坑位7(约定当从后向前或者从前向后遇到同一个空坑位即停止本轮次)并将基准数5填入该坑位7中,由于寻找的过程在5之前的数一定比5小,在5之后的数一定比5大。
一轮次循环的另一种讲解(更易于理解(利用指针)但是java中没有指针只是为了更便于理解。)看图易于理解不再具体分析!
代码块主要包含两个类一个是运行main函数的kuaiSuSort主类,另一个是用来定义main函数中运行的quickSort函数的QuickSortUtils类。代码如下
package paixu; import java.util.Arrays; public class kuaiSuSort { public static void main(String[] args) { //定义一个数组 int[] arr={10,3,5,6,1,0,100,40,50,8,-1,30,98}; //调用工具类,进行快速排序 传入数组,传入起始位置,传入结束位置在另一个类中定义的static函数 QuickSortUtils.quickSort(arr,0,arr.length-1); //输出排序后的结果 System.out.println(Arrays.toString(arr)); } }
package paixu; public class QuickSortUtils { public static void quickSort(int arr[],int start,int end){ //找出分左右两边的索引位置(如上述基准数5所在坑位7的职位),然后对左右两区进行递归调用 if(start<end){//这就是限制条件即到分区为1时结束 int index = getIndex(arr,start,end);//确定上述的索引位置 //对区分后的分区按同样方法进行递归调用 quickSort(arr,start,index-1); quickSort(arr,index+1,end); } } //具体思路就是上述的挖坑填数 private static int getIndex(int[] arr, int start, int end) { int i=start; int j=end; int x=arr[i];//所确定的基准数即首个数位置 while (i<j){//只是为了保证一轮结束后能够继续跑下去 while(i<j&&arr[j]>x){ j--; } if(i<j){//来确定是由arr[j]<=x才令循环停止故而找到了一个比x大的数且坑位没有重合 arr[i]=arr[j];//将找到的比x大的数填入上一个坑位 i++;//由于左边的坑位早就无数所以当从左向右遍历时,要加一个数 } //由前向后找比他大或等于的数,找到后挖出此数填到前一个坑中 while(i<j&&arr[i]<x){ i++; } if(i<j){ arr[j]=arr[i]; j--;//理由同上 } } arr[i]=x;//将基准数放在已经重合的i与j之中故而此时i与j均可 return i;//此时也是i与j均可 } }
本次代码主要运用了递归的思想,这一点在算法思想中也体现了出来,就是将一个数组按一个基准数分为一个全部比基准数大的区间和一个比基准书小的区间,再分别对两个区间进行该算法,直到每个区间都只剩下一个数时即完成了排序,同时在每次排序时候也伴随着数组元素位置的交换。
对于主类kuaiSuSort较为简单只包含一个定义的位于工具类QuickSortUtils的函数quickSort(arr,0,arr.length-1);即传入一个数组以及待排序数组的下标起始与末尾。且无返回值只是对于数组元素位置的改变。最后调用Arrays类的toString方法来实现对于数组内所有元素的打印。
而对于位于工具类的函数quickSort(int arr[],int start,int end)就是按照递归思想的调用,首先当满足条件start<end时调用一个函数getIndex(arr,start,end)对于传入其中的数组arr以及输入的起始以及末尾的下标来获得最终基准数要停止的位置也就是分界线的位置,最终再递归的对已经分好的两个分区递归调用quickSort函数即为 quickSort(arr,start,index-1);与quickSort(arr,index+1,end);函数。
而对于getIndex函数在确定基准数位置的同时还包含了对于数组位置交换的操作,首先最外层的while(i<j)就是挖坑填数时当从前往后以及从后往前的坑重合时候结束挖坑填数的操作,而内部则是针对每一轮的操作,首先再次之前已定义i=start,j=end并且令基准数x为首个元素即为x=arr[i];
首先对于从后向前的遍历条件为while(i<j&&arr[j]>x)之所以在内层循环依旧i<j的原因是i与j的位置一直不断变化所以内层也需要进行算法结束的判断,而arr[j]>x则是从后向前看均不是大于等于x的元素且i仍<j所以要继续向前找即令j--;当此循环停止时由于可能是i>=j(实际为i==j)造成的所以要用if语句来确保是由于arr[j]>=x所造成的,此时就可以进行数组元素位置的交换,即将数组下标为i与j的元素位置互换,因为i就是已经被挖走放进上一个j的位置,就算是第一个i也就是x基准数实际上也是被挖走而空着的位置,同时要令i++;目的是由于此时的j已经替换了原来的i一定为大于等于x的数(实际是为了避免为等于x的情况而不断进行互换而陷入死循环),故而要从下一个位置进行从前往后的遍历; 同理得从前往后的代码也类似区别在于while语句中包含i<j以及arr[i]<x;同时进行交换(arr[j]=arr[i])后为令j--;也是为了防止陷入死循环,当因为i==j而令算法结束时候不要忘了将首个挖出的基准数x放入此时空着的坑(i与j均可),即arr[i]=x;同时还要将i或j代表的数返回,即为要确定的分区的下标index,便于递归调用的进行。 这就是对于快速排序的算法,它调用了递归算法的思想,需要从快速排序的思想出发进行理解。