1、快速排序思想:
快速排序主要利用分治和递归的思想,例如,将一个数组{2,5,10,1,0,7}排序。首先选取其第一个元素2作为基准,将<2的元素移动到其左边,将>=2的元素移动到其右边(其中等于2放左放右都可以),此过程称为一次分区,可以得到{0,1,2,10,5,7}。经过一次分区后,数组就分成了两个部分,左边小于2,右边大于等于2,并且局部有序了一点。因此可以继续分别对其左右分区{0,1}和{10,5,7}再分别进行快排,不断的迭代,直至完全有序。
过程解析:写两个quickSort方法,一个是主程序调用入口,另一个是重载方法,重载的参数是迭代排序的范围。主要需要实现的partition分区方法,改方法主要利用头尾指针交替寻找比基准节点大或者小的数,并交换两个指针的值,以达到分区的目的。
2、代码实现
import java.util.Arrays; public class QuickSort { public static void main(String[] args) { int[] arr = {2, 5, 10, 1, 0, 7}; System.out.println(Arrays.toString(arr)); QuickSort quickSort = new QuickSort(); quickSort.quickSort(arr); System.out.println(Arrays.toString(arr)); } public void quickSort(int[] arr) { int start = 0; int end = arr.length - 1; quickSort(start, end, arr); } public void quickSort(int start, int end, int[] arr) { if (start < end) { int index = partition(start, end, arr); quickSort(start, index - 1, arr); quickSort(index + 1, end, arr); } } public int partition(int start, int end, int[] arr) { int k = arr[start]; while (start < end) { while (arr[end] >= k && start < end) { end--; } if (start < end) { arr[start] = arr[end]; start++; } while (arr[start] < k && start < end) { start++; } if (start < end) { arr[end] = arr[start]; end--; } } arr[start] = k; int index = start; return index; } }
import java.util.Arrays; public class QuickSort { public static void main(String[] args) { int[] arr = {2, 5, 10, 1, 0, 7}; System.out.println(Arrays.toString(arr)); QuickSort quickSort = new QuickSort(); quickSort.quickSort(arr); System.out.println(Arrays.toString(arr)); } //快排方法 public void quickSort(int[] arr) { int start = 0; int end = arr.length - 1; quickSort(start, end, arr); } //快排方法重载,带上数组应该排序部分的首尾指针,每次分区完成,都要缩小范围 public void quickSort(int start, int end, int[] arr) { //递归,递归的终止条件,不断的分区排序,知道不能再分区为止 if (start < end) { //将数组继续拆分成两个分区,继续进行分区 int index = partition(start, end, arr); //将数组继续拆分成两个分区,继续进行分区 quickSort(start, index - 1, arr); quickSort(index + 1, end, arr); } } //分区方法,局部排序 public int partition(int start, int end, int[] arr) { //选择数组的第一个元素为基准点,并将基准点值保存到k中 int k = arr[start]; //将小于基准点的元素移动至数组的左边,大于的移动至右边 //循环终止条件,只要有头尾指针的,一般都是这个,其实遍历完了整个数组就终止了 while (start < end) { //从数组的右边开始,找比基准点小的数,将指针指向该数 //右边开始遍历,找到第一个即停止 while (arr[end] >= k && start < end) { end--; } //上面指针指向了比基准点小的元素,接下来就将该元素移动到基准点的左侧 //放置的位置是头指针指向的位置,头指针向后移动一位 if (start < end) { arr[start] = arr[end]; start++; } //同理,从左至右找比基准点大的元素,找到了循环就先暂停 while (arr[start] < k && start < end) { start++; } //交换位置,将该值交换到尾指针指向的位置,因为尾指针的值已经赋给了之前的头指针 //尾指针向前移位 if (start < end) { arr[end] = arr[start]; end--; } //如此反复循环,不断将遇到的第一个符合条件的元素交换至对应侧 } //记得将基准点元素的值还回去,循环结束指针指向的位置就是基准点的最终位置 arr[start] = k; //记录循环结束后头尾指针的位置,头尾指针肯定在一个位置上,因为循环结束了start=end int index = start; //返回该位置 return index; } }
3、时间空间复杂度
时间复杂度:平均时间复杂度为O(nlogn),最坏时间复杂度O(n^2)。
空间复杂度:每次递归传参left,和right,平均递归次数是log(n)次,所以平均空间复杂度是O(log(n))。最坏的情况是O(n)(初始是逆序的情况)。