算法(二):快速排序的两种实现方法

快速排序的原理是有序的数组(顺序)一定满足这样的充分必要条件,对所有的元素而言(亦即每一个元素都可以作为基准元素),每一个比自己大的元素都排列在自己的身后,每一个比自己小的元素都排列在自己的身前。

所以,快速排序的步骤是:
1. 选定一个基准元素(有很多种方法,如随机、第一个元素、中间的元素……,随你所愿);
2. 创建两个游标,一个用于从后向前检索比基准元素小的元素,一个用于从前向后检索比自己大的元素;
3. 如果检索到了这样的元素,则进行交换,交换的规则是:

假定小元素的位置j,大元素的位置为i,基准为k,基准元素为p,数组为a,那么步骤是:
1. 小的元素放置在基准位置,即a[k] = a[j], k = j;
2. 小的元素位置放置大的数据,即a[k] = a[i], k = i;
3. 新的基准位置放置基准数据,即a[k] = p;

4. 不断重复步骤2,直到两个游标相遇;
5. 在游标相遇的位置划分出2个新的排序区间,然后对每个区间进行1-4步,直到划分不出新的区间为止;

根据上面的步骤,我们可写出第一种写法(递归写法):

static int sort(int[] nums, int start, int end) {
    //  选定基准元素
    int pivot = nums[start];
    int k = start;
    while(end > start) {
        while(end > start && nums[end] >= pivot) {
            end --;
        }
        if(end > start) {
            nums[k] = nums[end];
            k = end;
        }
        //  排除基准元素(即第一个元素),需要使用<=判断
        while(end > start && nums[start] <= pivot) {
            start ++;
        }
        if(end > start) {
            nums[k] = nums[start];
            k = start;
        }
        nums[k] = pivot;
    }
    //  返回相遇位置
    return k;
}

//
static void quickSort(int[] nums, int start, int end) {
        int mid = sort(nums, start, end);
        //  如果只有一个元素,没有继续的必要
        if(mid - 1 > start) {
            quickSort(nums, start, mid - 1);
        }
        //  如果只有一个元素,没有继续的必要
        if(mid + 1 < end) {
            quickSort(nums, mid + 1, end);
        }
}

根据上面的代码,我们可以很容易写出第二种实现方法——循环实现方法:

static void quickSortArr(int[] nums) {
    //  作为辅助数据结构
    Stack<int[]> stack = new Stack<>();
    //  放入两个索引的位置 
    stack.push(new int[] {0, nums.length - 1});
    while(!stack.isEmpty()) {
        int[] index = stack.pop();
        int start = index[0];
        int end = index[1];
        //  第一种解决办法
        //  可以避免<= 判断,见下面的第二种方法
        int i = start + 1;
        int j = end;
        int k = start;
        int pivot = nums[start];
        while(i < j) {
            while(i < j && nums[j] > pivot) {
                j --;
            }
            if(i < j) {
                nums[k] = nums[j];
                k = j;
            }
            //  第二种解决办法
            //  避免起始位置死循环的方法
            //  while(i < j && nums[i] <= pivot)
            while(i < j && nums[i] < pivot) {
                i ++;
            }
            if(i < j) {
                nums[k] = nums[i];
                k = i;
            }
            nums[k] = pivot;
        }
        if(k - 1 > start) {
            stack.push(new int[] {start, k - 1});
        }
        if(k + 1 < end) {
            stack.push(new int[] {k + 1, end});
        }
    }
}

结论

快速排序的难点在于对两个扫描索引的理解,尤其是相遇时就是基准元素的正确位置,理解了这个知识点,快速排序就易于反掌。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值