快速排序详解
快速排序:可以对一个数组或者是对数组中的一段进行排序
思想: 选择一个base, 然后通过一顿瞎操作, 使得 当前数组变成 左小,base, 右大(小,大,都是相对于base 来说的)
然后使用同样的方法将 上一步的左小排成 左小,base, 右大的格式, 将上一步的右大也排成 左小,base, 右大的格式
这样不断的进行,最终变成了一个有序的数组(从思想中可以发现这个是将大问题不断转换为一个个的小问题,所以可以
使用递归实现)
实现: 因为上面说了这个可以通过递归实现, 所以我只需要弄清楚一层的 左小,base, 右大的实现即可
选择数组最左边的数为base,然后生成两个指针left, right,分别指向数组的左边和右边,根据左边指针的作用
是将左边的数都变成比base 小的数, 所以左边指针left 在移动的时候,发现比base 大的数字的时候,异常出现了
同理右边指针right的作用是将右边的数字都变成比base 大的数, 所以右边指针right 移动的时候,发现比base 小的
数字的时候, 异常出现了,那么怎么处理呢? 将left 指向的数字,和right 指向的数字进行相互的交换, 就使得左右两边暂时满足
开始的愿望, 左小右大,直到两个指针相遇的时候。两个指针相遇了, 那么这个不是之前的异常了,问题出现了,
两者相遇的时候,这个值为多大,是比base大的,还是小的,怎么处理。还有上面的指针动的先后顺序有没有讲究?
(下面有解答) 然后使用递归思想进行全部的排序,解决问题。
一、递归的最小化问题的终止条件是什么?
答: 终止条件是: 当left 和 right 一开始就相同的时候, 而不是left < right , 那么就是终止,不需要进行排序了
即排序的元素为一个或者为空的时候, 则不进行排序了, 返回了。
二、第一个为什么选择左边或者是右边作为基底?
答:是因为这样很方便,就不需要随机的去选择一个数字
三、 为什么左边为base, 则右边的指针先动, 右边的为基底时, 则左边的先动
答: 首先, 明确一点,两个指针相遇的时候,一定是一个指针遇到另外一个指针,即一个指针多走了一步,然后两个相遇了
所以, 先走的那个指针就会与后走的那个指针进行相遇了(在两个元素的时候,这个句话有问题, 所以成立的条件时候大于
三个元素的时候)因为,左base 时候,右边的指针与右边指针相遇的时候, 右边指针对应的值一定是小于base的,即两者相遇
的指针所对应的值是小于base的, 所以直接可以交换base和交点位置的元素, 满足左小, base , 右大的格局
同理: 右边为base 的时候, 左边的指针开始进行移动,所以最终两个指针相遇的地方对应的数字一定是大于base的, 所以
可以直接交换base 和交点上数值, 满足 左小, base, 右大的原则。
总结: 快速排序的口诀,一选底, 二动指针分先后, 左基底右先行, 右基底左先行,指针相遇换基底,问题化小转递归,终止条件0,1 (个)数。
class QuickSourt { public static void main(String []args) { int[] arr = {2,4,7,9,2,4,6,79,1,23,45,23,7,23}; System.out.println("hello"); qucikSort(arr, 0, arr.length-1); System.out.println("hello"); for(int i = 0; i < arr.length; i ++){ System.out.println(arr[i] +" "); } } public static void qucikSort(int[] arr, int left, int right){ // 递归的终止条件 if(left > right){ return; }
int base = arr[left]; // 设置基底 int i = left; int j = right; while(i != j){ // 直到找到一个 比 base 小的数字 while(arr[j] >= base && i < j){ // 这边这个限制条件还是不错的 j--; } // 直到找到一个比 base 大的数字 while(arr[i] <= base && i < j){ i ++; } int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } arr[left] = arr[i]; arr[i] = base; // 然后使用递归再不断的自己排自己的左边和右边的 qucikSort(arr, left, i - 1); qucikSort(arr, i + 1, right); } }