排序算法----快速排序

       快排运用的基本思想是经典的荷兰国旗问题。就是给定一个无序数组和一个数(在数组中出现),将数组中大于给定数的放在左边(小于区),等于给定数的放在中间(等于区),大于给定数的放在右边(大于区)。然后在小于区和大于区继续这一过程,不断递归进行,直到最终小于区、大于区只有一个元素为止。

       为什么时间复杂度都是O(NlgN),相比于堆排序,归并排序,快排更加受到青睐呢?这是因为快排的处理逻辑很简单,导致其常数项很小,运行时间较其他时间复杂度相同的排序算法要小。但是要注意快排无法做到稳定性,比如要对一些自定义类的许多实例进行排序,此时要考虑排序的稳定性,使用快排就不合适了。而像基本数据类型的排序,不考虑稳定性,这时快排就是一个很好的选择。

       快排中每一趟partition过程中影响效率的一个关键是枢纽点的选择。选择的不好,partition过后小于区、大于区的大小严重不平衡,这样会造成快排的退化,时间复杂度可能到O(N)。最好的枢纽点选择是partition过后小于区、大于区的大小基本相等。

       根据枢纽点的选择不同,快排可以分为经典快排和随机快排。经典快排每次选择最后一个元素作为枢纽点(存在极大的退化风险),随机快排是每次随机选择一个元素作为枢纽点,长期来看,这是一个概率问题,数学证明长期随机快排的时间复杂度为O(N)。下面代码是将经典快排的基础上进行优化,成为一个简单随机快排。

     

package young.unit01;

/**
 * 随机快速排序
 * 时间复杂度:|最坏O(N^2):就是选取的轴点极差,每次只能确定一个位置,其他数都大于或者小于它,问题规模每次只是小于1
 *         |最好及长期期望(O(NlgN)):选取的轴点很好,左右区域大小几乎一致,相当于每次将问题规模缩减一半
 * 空间复杂度:|最坏O(N)---每次确定一个位置,需要一个变量记住位置,递归n次,
 *         |最好O(lgN)---每次记住小于区域右边界,大于区域左边界;递归lgN次
 *         
 * 稳定性:一般可以说做不到稳定性,partition过程很难做到稳定性
 * 例如:  |7 3 6 0 0|3
 *      r        l
 *      第一个7>3,所以第一个0与大于区域左边第一个数0交换,第二个0就到第一个0前面了
 *      ...
 *      遍历到之前第一个0时,0<3,所以这个0与小于区域右边第一个数交换,此时这个0还是位于第二个0后面
 *      两个0的相对顺序不可能再回归原来次序
 *      做不到稳定性
 *      
 * 论文级别可以做到快排稳定性(0,1 stable sort)
 * @author sunmin
 * @Description:TODO
 * @date:2018年6月17日    下午1:16:14 
 * @Copyright: 2018 www.xy.com. All rights reserved. 
 * 内部代码,严禁外泄
 */
public class T10_QuickSort {

    public static void quickSort(int[] nums, int left, int right) {
        if (left < right) {
            swap(nums, left + (int)(Math.random() * (right - left + 1) / 2), right);
            int[] p = T09_Partition.partition(nums, left, right);
            T09_Partition.partition(nums, left, p[0] - 1);
            T09_Partition.partition(nums, p[1] + 1, right);
        }
    }
    
    private static void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
}
package young.unit01;

import java.util.Arrays;

/**
 * 荷兰国旗问题
 * @author sunmin
 * @Description:TODO
 * @date:2018年6月17日    下午1:00:50 
 * @Copyright: 2018 www.xy.com. All rights reserved. 
 * 内部代码,严禁外泄
 */
public class T09_Partition {
    
    public static int[] partition(int[] nums, int left, int right) {
        int less = left - 1;
        int more = right;
        int p = nums[right];
        while (left < more) {
            if (nums[left] < p) {
                swap(nums, ++less, left++);
            } else if (nums[left] > p) {
                swap(nums, --more, left);
            } else {
                left++;
            }
        }
        swap(nums, more++, right);
        return new int[]{less + 1, more - 1};
    }
    
    private static void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }

    public static void main(String[] args) {
        int[] a = new int[]{3, 6, 5, 7, 2, 9, 1, 5, 5};
        int[] k = partition(a, 0, a.length - 1);
        System.out.println(Arrays.toString(a));
        System.out.println(Arrays.toString(k));
    }
}

 

posted @ 2018-07-17 16:16  大将军姜伯约  阅读(159)  评论(0)    收藏  举报