排序算法之快速排序
介绍
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
也就是随便选择一个数字,然后根据这个数字进行拆分数组。和合并排序有些像,合并排序也是计算出一个下标,而合并排序是从中间拆开。
快速排序类似,选择一个序列中的数字,比这个数字大的放到左边,比这个数字小的则放在右边。和这个数字相等的放在中间,然后在合并三个序列,左+中+右。递归进行就可以得到有序数组。
代码实现:
依然是新建个方法
public static int[] quickSort(int[] A){}
因为是递归所以首先终止条件
if(A.length<=1){ return A; }
选择一个数组,并声明记录左、中、右小标和数组。
// 选择一个数字 int selectNum = A[0]; // 记录左边下标 int leftIndex=0; // 记录右边边下标 int rightIndex=0; // 记录中间下标 int midIndex=0; // 声明三个数组 int[] leftArray =new int[A.length]; int[] rightArray =new int[A.length]; int[] midArray =new int[A.length];
进行遍历原数组,拆分。
// 进行循环对比,如果大于的则放左边,小于的则放右边,不大于不小于的则放中间。 for (int i = 0; i < A.length; i++) { if(A[i]>selectNum){ rightArray[rightIndex++]=A[i]; }else if(A[i]<selectNum){ leftArray[leftIndex++]=A[i]; }else{ midArray[midIndex++]=A[i]; } }
因为之前声明的数组是总数组的长度。所以数组有溢出,进行数组的截取。如果没有储存数据则把数组置空,因为声明的时候数组中有多个0,左右分别递归快速排序
// 如果左边数组有,则进行截取,因为声明数组的时候是全数组长度,有溢出。截取多余的0 if(leftIndex>0){ leftArray = Arrays.copyOfRange(leftArray,0,leftIndex); // 递归快速排序 leftArray = quickSort(leftArray); }else{ // 如果没有进行存储数组,则把数组置空,因为声明的时候数组里面有多个000000 leftArray = new int[]{}; } if(rightIndex>0){ rightArray = Arrays.copyOfRange(rightArray,0,rightIndex); rightArray = quickSort(rightArray); }else{ rightArray = new int[]{}; } if(midIndex>0){ midArray = Arrays.copyOfRange(midArray,0,midIndex); }else{ midArray = new int[]{}; }
合并数组:
// 声明数组用于储存分好的三个数组。 int[] B = new int[leftArray.length+midArray.length+rightArray.length]; // 依次把左、中、右存放到数组中 System.arraycopy(leftArray,0,B,0,leftArray.length); System.arraycopy(midArray,0,B,leftArray.length,midArray.length); System.arraycopy(rightArray,0,B,leftArray.length+midArray.length,rightArray.length);
然后返回该数组,
完整代码:
public static int[] quickSort(int[] A){ if(A.length<=1){ return A; } // 选择一个数字 int selectNum = A[0]; // 记录左边下标 int leftIndex=0; // 记录右边边下标 int rightIndex=0; // 记录中间下标 int midIndex=0; // 声明三个数组 int[] leftArray =new int[A.length]; int[] rightArray =new int[A.length]; int[] midArray =new int[A.length]; // 进行循环对比,如果大于的则放左边,小于的则放右边,不大于不小于的则放中间。 for (int i = 0; i < A.length; i++) { if(A[i]>selectNum){ rightArray[rightIndex++]=A[i]; }else if(A[i]<selectNum){ leftArray[leftIndex++]=A[i]; }else{ midArray[midIndex++]=A[i]; } } // 如果左边数组有,则进行截取,因为声明数组的时候是全数组长度,有溢出。截取多余的0 if(leftIndex>0){ leftArray = Arrays.copyOfRange(leftArray,0,leftIndex); // 递归快速排序 leftArray = quickSort(leftArray); }else{ // 如果没有进行存储数组,则把数组置空,因为声明的时候数组里面有多个000000 leftArray = new int[]{}; } if(rightIndex>0){ rightArray = Arrays.copyOfRange(rightArray,0,rightIndex); rightArray = quickSort(rightArray); }else{ rightArray = new int[]{}; } if(midIndex>0){ midArray = Arrays.copyOfRange(midArray,0,midIndex); }else{ midArray = new int[]{}; } // 声明数组用于储存分好的三个数组。 int[] B = new int[leftArray.length+midArray.length+rightArray.length]; // 依次把左、中、右存放到数组中 System.arraycopy(leftArray,0,B,0,leftArray.length); System.arraycopy(midArray,0,B,leftArray.length,midArray.length); System.arraycopy(rightArray,0,B,leftArray.length+midArray.length,rightArray.length); return B; }
List进行快速排序
其实数组可以用List代替,使用java8新特性代码会变得简单很多。如下:
public static List<Integer> quickSort(List<Integer> A){ if(A.size()<=0){ return A; } // 选择一个数字 Integer select = A.get(0); // 获取小于选择数字的集合 List<Integer> leftList = A.stream().filter(x->x<select).collect(Collectors.toList()); // 获取大于选择数字的集合 List<Integer> rightList = A.stream().filter(x->x>select).collect(Collectors.toList()); // 获取等于选择数字的集合 List<Integer> minList = A.stream().filter(x-> x.equals(select)).collect(Collectors.toList()); // 左右进行重新排序 leftList=quickSort(leftList); rightList=quickSort(rightList); // 合并集合 List<Integer> resultList = new ArrayList<>(); resultList.addAll(leftList); resultList.addAll(minList); resultList.addAll(rightList); return resultList; }
逻辑大概与上面代码一样。
网上有个快速排序的动态图,

在一个课程上面又看到另一中快速排序,但是有局限性。选择元素必须是序列的最左边的元素。
代码如下:
public void sort(int[] A) { this.quickSort(A, 0, A.length); } private void quickSort(int[] A, int l, int r) { if(r - l <= 1) { return; } // 拆分序列,左边大于选择的,右边小于选择的,得到的i就是截取的下标 int i = partition(A, l, r); // 拆分后的元素进行重新快排 quickSort(A, l, i); quickSort(A, i+1, r); } private int partition(int[] A, int l, int r) { // 选取元素 int x = A[l]; // 序列类似是这种 |X|left----|right--| // l+1也就是从第二个元素,越过x开始比较 int i = l + 1; // 元素的终止下标 int j = r; while(i != j) { // 如果当前遍历元素小于x,则跳过,因为是从左往右开始遍历所以小于x的就在右边。i++ 下一个遍历 if(A[i] < x) { i++; } else { // 如果当前遍历元素大于x,则和最后一个元素换位置, // 也就是把大于选择元素的那个放在最后面。y--也就是最后一个已经排序好了不进行遍历排序。 swap(A, i, --j); } } swap(A, i-1, l); return i-1; } private void swap(int[] A,int x,int y){ int tmp = A[x]; A[x] = A[y]; A[y] = tmp; }

依次类推进行递归。
举个例子,如果选的是23的话,23和5对比,则5小于23,i++然后进行下个循环,如下所示:


浙公网安备 33010602011771号