算法和数据结构--快速排序

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Xtick/article/details/82804524

文章主要针对自己在理解快速排序过程中当时卡壳的点,记录了接触这个算法的经历,不是对快速排序算法原理的分析和理解。

两种思维

我在网上找到了两种理解快速排序比较好的思维方法,具体的思路写在了注释中:

方法一:

 private static void quickSort1(int[] nums, int start, int end) {
        //递归出口,千万别忘了,当start==end的时候,代表只剩一个元素了,不需要再进行排序
        if (start >= end) {
            return;
        }
        //基准
        int point = nums[start];
        int i = start;
        int j = end;
        int t;
        while (i < j) {
            //从右向左进行扫描,找到第一个小于基准的值的位置,
            //之所以先从右向左进行,是为了后面swap处作铺垫
            while (nums[j] >= point && i < j) {
                j--;
            }
            //从左向右进行扫描,找到第一个大于基准的值的位置
            while (nums[i] <= point && i < j) {
                i++;
            }
            //如果 i<j 那么就进行交换,i<j 说明左边还是可能存在大于基准的值,还需要继续扫描,
            //直到 i==j 的时候说明已经满足“左边的值都小于基准,右边的值都大于等于基准”的约定,
            // i==j 的时候就会跳出外层的while循环,进入swap
            if (i < j) {
                t = nums[i];
                nums[i] = nums[j];
                nums[j] = t;
            }
        }
        //---------------------------swap--------------------------
        //这里会将 j 和基准进行交换,这里就能看出上面描述的先从右到左进行扫描的目的,
        // j 恰好小于基准,所以与基准交换后刚好满足“左边的值都小于基准,右边的值都大于等于基准”的约定
        nums[start]=nums[j];
        nums[j]=point;
        //---------------------------------------------------------
        System.out.println("Sorting: " + Arrays.toString(nums));
        quickSort1(nums, start, j - 1);
        quickSort1(nums, j + 1, end);
}

参照文章:【坐在马桶上看算法】算法3:最常用的排序——快速排序


方法二:

private static void quickSort2(int[] arr, int low, int high) {
        if (arr.length <= 0) return;
        if (low >= high) return;
        int left = low;
        int right = high;
        //挖坑1:保存基准的值
        int temp = arr[left];   
        while (left < right) {
            while (left < right && arr[right] >= temp) {  
                right--;
            }
            //挖坑2:从后向前找到比基准小的元素
            //填坑1:插入到基准位置坑1中
            arr[left] = arr[right];
            while (left < right && arr[left] <= temp) {   
                left++;
            }
            //挖坑3:从前往后找到比基准大的元素
            //填坑2:放到刚才挖的坑2中
            arr[right] = arr[left];
        }
        //填坑3:基准值填补到坑3中,准备分治递归快排
        arr[left] = temp;   
        System.out.println("Sorting: " + Arrays.toString(arr));
        quickSort2(arr, low, left - 1);
        quickSort2(arr, left + 1, high);
}

参照文章:八大排序算法总结与java实现


总结

  • 对于我来说,方法一是一个交换过程,而方法二是一个挖坑填坑的过程。之所以会产生这两种感觉的原因,我觉得是他们的值交换触发的时间点不同导致的。方法一是统一进行标记,标记结束后再一次性的进行交换;而方法二是在每次扫描之后就会进行交换。
  • 这里有一个点,我之前一直把扫描的范围设在start+1和end之间,把基准给排除出去了,而且一直以为很合理,但是使用{3, 3, 3, 3, 1, 6, 5}用例进行测试时总是出错,通过断点分析发现,如果扫描的数组恰好已经是有序的了,比如{1,2,3,4,5,6},那么在从后向前扫描的时候,j 的位置会停在start+1的位置上,即 j=i 的位置上,此时跳出外层的while循环,基准位和 j 位置的值进行交换,这样显然不合理,因为start的值根本没和start+1(也即是 j 最终的位置)进行过比较(这里j的while循环跳出是因为不满足了i<j的条件,而不是因为不满足nums[j]>=point),就进行了交换,这样的结果就是原来明明已经有序的数组又变成无序的了。

参考资料:

猜你喜欢

转载自blog.csdn.net/Xtick/article/details/82804524