快速排序while循环先执行high--的原因

Posted 任猜何意

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速排序while循环先执行high--的原因相关的知识,希望对你有一定的参考价值。

当前浏览器不支持播放音乐或语音,请在微信或其他浏览器中播放 快速排序while循环先执行high--的原因

今天复习排序算法,看到快速排序的时候,根据自己印象中的排序思想先写了段代码:
1.快 排序 算法思想

快速排序是一种使用分治法的排序算法:每次从无序数列中选取一个基准元素,然后将数列以基准元素为边界划分为两个子数列,基准元素左侧的数列所有元素都比基准元素小,右侧数列的所有元素都比基准元素大,然后再对两个子数列做做同样的划分,直到子序列无法再分为止。
2.误将w hi le循环先 low指针 右移,然后high指针左移

根据上面的基本思想,就直接写了段代码测试:
 
   
   
 
package com.lancao.learn.hashmap;
import org.junit.Test;
/** * 排序算法测试 */public class SortTests {
@Test public void testSort() { int[] array = new int[]{10, 8, 3, 40, 2, 5, 3, 6, 4, 7, 9}; // 简单冒泡排序// simpleBubbleSort(array); // 鸡尾酒排序// cocktailSort(array); // 快速排序 quickSort(array, 0, array.length - 1); System.out.println("排序后的数列:"); // 打印排序后的数组 printArray(array); }
private static void printArray(int[] array) { for (int i = 0; i < array.length; i++) { System.out.print(array[i] + ", "); } }
/** * 快速排序 */ private static void quickSort(int[] array, int start, int end) { if (start >= end) { return; } int mid = partition(array, start, end); quickSort(array, start, mid - 1); quickSort(array, mid + 1, end); }
private static int partition(int[] array, int start, int end) { int low = start; int high = end; int pivot = array[low]; while (low < high) { while (low < high && array[low] <= pivot) { low++; } while (low < high && array[high] > pivot) { high--; } if (low < high) { int temp = array[low]; array[low] = array[high]; array[high] = temp; } } array[start] = array[low]; array[low] = pivot; return low; }}

然后跑了下test,神奇的事情发生了:

快速排序while循环先执行high--的原因
  说好的排序后的 数列呢?还是个乱序啊!
难道我理解错了,不就是把小值放基准左边,大值放基准右边吗?这思路好像没错啊,去看了书上的代码和网上的代码,发现基本上大同小异,跟我写的这段差别也不大,唯一不同的就是我代码中红色代码在绿色代码段之前,而别人的都是绿色这段在红色那段之前,难道这点差别真的影响这么大吗?我不信,于是我把这两段代码调整了下顺序,更神奇的事情发生了——竟然真的正确排序了
快速排序while循环先执行high--的原因
3.正确的while循环先low指针右移,然后high指针左移

这……激起了我不信邪的决心,反复调整这两段代码测试,结果却是每次都是同样的结果:先low++就一定排序混乱,先high--就绝对排序正确。这下不信邪真的不行了。 带着强烈的自我质疑感,手动在纸上演算了一遍这个算法,发现问题出在while(low<high)大循环结束后pivot定位的时候:
因为最初是以start位置的元素作为基准元素的,所以最后要将基准元素赋值给中间元素的时候,start的位置将处于基准元素的左侧,根据排序算法基本思想(基准元素左侧子数列所有元素都小于等于基准元素)要求,start位置上的新元素应该是一个小于基准元素的数据。但是我们 先将low指针右移的时候,却将low指针最后定位在了第一个比基准元素大的元素上,最后把它放在start的位置上,这显然是 打破了 算法思想要求的 因此先将low指针左移一定会导致人排序失败(就算你本来是一个有序数列,经过它排序之后都变成一个乱序数列!)。
知道了错误的根源所在,如果你是一个严格喜欢从左到右按部就班做事的人的话,那么你就可以实现你的“while循环中先执行low指针右移”方式的代码了: 因为原来是因为基准元素使用了最左元素而注定while循环内必须先执行high指针左移的,那么我们使用最右侧的元素作为基准元素,自然也就决定了我们的“while循环内必须先执行low指针右移”的原则
package com.lancao.learn.hashmap;
import org.junit.Test;
/** * 排序算法测试 */public class SortTests {
@Test public void testSort() { int[] array = new int[]{10, 8, 3, 40, 2, 5, 3, 6, 4, 7, 9}; // 简单冒泡排序// simpleBubbleSort(array); // 鸡尾酒排序// cocktailSort(array); // 快速排序 quickSort(array, 0, array.length - 1); System.out.println("排序后的数列:"); // 打印排序后的数组 printArray(array); }
private static void printArray(int[] array) { for (int i = 0; i < array.length; i++) { System.out.print(array[i] + ", "); } }
/** * 快速排序 */ private static void quickSort(int[] array, int start, int end) { if (start >= end) { return; } int mid = partition(array, start, end); quickSort(array, start, mid - 1); quickSort(array, mid + 1, end); }
private static int partition(int[] array, int start, int end) { int low = start; int high = end; int pivot = array[high]; while (low < high) { while (low < high && array[low] < pivot) { low++; } while (low < high && array[high] >= pivot) { high--; } if (low < high) { int temp = array[low]; array[low] = array[high]; array[high] = temp; } } array[end] = array[high]; array[high] = pivot; return high; }}

注意紫色框内的代码是与因为取了右侧元素为基准元素而与原代码不同的地方,跑下test,排序结果当然是无比正确的!
快速排序while循环先执行high--的原因
事实证明,while循环内的代码究竟应该先执行low指针右移还是high指针左移完全取决于你的最初对基准元素的取值位置。 以后再也不用为这个问题而迷惑了。
(完)
快速排序while循环先执行high--的原因


“扫码关注”






以上是关于快速排序while循环先执行high--的原因的主要内容,如果未能解决你的问题,请参考以下文章

排序 快速排序-双路排序

排序算法——快速排序

快速排序

快速排序 c++ while(low<high&&a[high]>=pivotkey) --high; 实现的是啥功能???这句啥意思???

考研-快速排序

快速排序