快速排序算法的三种实现

1.普通快速排序

快速排序思路:随机取一个标定点 v,将 v 放置到合适的位置,保证 v 左边的元素都小于等于 v,v 右边的元素都大于 v,然后再继续分别对左边元素和右边的元素做同样的排序动作,直到整个数组有序。

那么怎么实现这个快速排序呢,其实也很简单,我们每次取数组的第一个元素为 v,然后从 v 后面的第一元素开始遍历数组(j) ,如果当前元素小于等于 v,则交换当前元素和 j 位置的元素,并且 j++,如果当前元素大于 v,则直接遍历下一个元素就好。遍历完整个数据后,我们再将 v 和 j -1位置的元素交换位置就可以了。

具体的实现:

public class QuickSort1 {
    // 方法入口
    public void sort(int[] arr){
        innerSort(arr, 0, arr.length - 1);
    }
	
    // 不断递归调用左右两边的数组
    private void innerSort(int[] arr, int l, int r){
        if(l >= r) return;
        int p = partition(arr, l, r);
        innerSort(arr, l, p - 1);
        innerSort(arr, p + 1, r);
    }
	
    // 快速排序的核心逻辑
    private int partition(int[] arr, int l, int r){
        // 优化点:对于基本有序的数组时间复杂度会降低到 O(n*n),这里随机选择v的值
        swap(arr, l, new Random().nextInt(r - l + 1) + l);
        
        int v = arr[l]; 
        int j = l + 1;
        for(int i = j; i <= r; i++){
            if(arr[i] <= v){
                swap(arr, i, j);
                j++;
            }
        }
        swap(arr, l, j - 1);
        return j - 1;
    }
    
    // 交换数组中两个元素的位置
    private void swap(int[] arr, int i, int j) {
        if (i == j) return;
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

2.双路快排

上面实现的快速排序算法,在有很多重复元素的时候,可能会导致标定点元素左边和右边的数组长度极度不平衡,在最坏的情况下,时间复杂度可能会退化成 O(n^2),所以我们下面来看新的一种思路,就是用两个指针,分别从数组的左右两边向中间走,左边的指针为 i,右边的为 j,当 i 下标代表的值大于等于 v 时,和 j 下标代表的值小于等于 v 时,i 和 j 交换位置,直到 i 和 j 相遇。

具体实现:

// 其他步骤同上面一样,这里只展示 partition 的实现
private int partition(int[] arr, int l, int r){
    // 优化点:对于基本有序的数组时间复杂度会降低到 O(n*n),这里随机选择v的值
    swap(arr, l, new Random().nextInt(r - l + 1) + l);
    int v = arr[l];
    int i = l + 1;
    int j = r;
    while(i <= j){
        while(i <= j && arr[i] < v){
            i++;
        }
        while(j >= i && arr[j] > v){
            j++;
        }
        if(i <= j){
        	swap(arr, i, j);
            i++;
            j--;    
        }
    }
    swap(arr, l, i - 1);
    return i - 1;
}

3.三路快排

还有一种最为经典的快速排序算法,就是三路快速排序,之前我们都是将数组分为两部分,小于等于 v 和大于 v 的,三路快速排序将数组分为小于、等于、大于 v 三部分,这样在继续递归的时候就不需要考虑等于 v 的部分了,整体性能会更好,下面是示意图:

// 由于 partition 操作要返回两个下标,所以这里直接把 partition 操作放到 innerSort 中
private void innerSort(int[] arr, int l, int r) {
    if (l >= r) return;
    swap(arr, l, new Random().nextInt(r - l + 1) + l);
    int v = arr[l];
    int lt = l;
    int gt = r + 1;
    int i = l + 1;
    while (i < gt) {
        if (arr[i] == v) {
            i++;
        } else if (arr[i] < v) {
            swap(arr, i, lt + 1);
            lt++;
            i++;
        } else {
            swap(arr, i, gt - 1);
            gt--;
        }
    }
    swap(arr, l, lt);
    innerSort(arr, l, lt - 1);
    innerSort(arr, gt, r);
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值