排序算法之快速排序

排序算法之快速排序

上一篇我们有介绍归并排序,我们知道归并排序是利用分治思想解决的,其实快速排序也是利用分治思想解决的,但是实现思路上却又完全不同了。快速排序的思想是,从数组中找到一个点记为pivot,把小于pivot的放到他的左边,把大于pivot的放到他的右边。

核心思想

分治思想的代码实现是可以用递归去做的,这里我们推导一下递归公式,显然,根据分治思想,要把这个数组一直往下分,同时更换数据位置,直到无法再分的时候,数组自然就有序了。
公式:quick_sort(p...r)=quick(p...q-1)+quick(q+1...r);
终止条件:p>=r

代码实现

package com.test;

import java.util.Arrays;

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

    static void quickSort(int[] arr, int p, int r) {
        if (p >= r) {
            return;
        }
        int q = partition(arr, p, r);
        quickSort(arr, p, q - 1);
        quickSort(arr, q + 1, r);
    }

    /**
     * @param arr, p, r
     * @return: int
     * @Title:
     * @Description: 选择一个pivot点, 进行分区, 我们把数组视为已处理区和未处理区
     * @date: 21:06 2021/3/22 0022
     * Modification History:
     * Date                Author        Description
     * -------------------------------------------*
     * 21:06 2021/3/22 0022      xx.huang       修改原因
     */
    static int partition(int[] arr, int p, int r) {
        //分区点的值
        int pivot = arr[r];
        //i标记已处理区的最后一个元素
        int i = p;
        //如果arr[j] < pivot,那么就把arr[j]放到已处理区的最后
        for (int j = p; j <= r - 1; j++) {
            if (arr[j] < pivot) {
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
                i++;
            }
        }
        int temp = arr[r];
        arr[r] = arr[i];
        arr[i] = temp;
        return i;
    }
}

我们重点分析partitionpartition方法的主要作用是找出分区点pivot,我们一般把数组最后一个数字视为pivot,然后我们处理的时候把数组视为已处理区和未处理区,已处理区的所有数字都是小于pivot的,我们用i来记录已处理区的结尾,用j来记录未处理区的开头,一旦未处理区的数字小于pivot,我们就把这个数字放到已处理区的结尾,当遍历完数组以后,此时应当把pivot添加到已处理区的结尾,这样一来pivot左边的数字就小于它,右边的数字也大于它了。然后根据递归思想,接下来就是把pivot左边的数组和右边的数组再如此反复处理,这样最后的数组就是有序的了。例如{3,5,1,4,9},在经过第一次处理的时候,因为我们选定pivot为9,所以数组没有变化,此时我们开始处理pivot左边的数组,也就是{3,5,1,4},选定pivot为4,经过处理,{3,1,5,4},此时4应当添加到已处理区后面,也就变成了{3,1,4,5},pivot左边的数组为{3,1}处理后变成{1,3},右边只有一个数字所以不用处理,最后合并数组就是{1,3,4,5,9},这里所谓的视为已处理区和未处理区,实际上并没有把数组真的拆开,就和归并排序一样,并没有拆数组,都是在原数组上面进行的操作。
例题:https://leetcode-cn.com/problems/smallest-k-lcci/
这道题解法是利用快速排序过程中的特点,即每完成一次排序的时候,pivot左边的数字都小于他,假设arr[k-1]=pivot,那么pivot就是倒数第k小的数字,我们想要获取最小的k个数,也就是当某次递归的排序中,出现q==k的情况,就可以终止递归,从数组取出前k个数字就是了。代码如下

package com.controller;

import java.util.Arrays;

public class LeetCode1714 {
    public static void main(String[] args) {
        int[] arr = new int[]{3, 3, 2, 5, 3, 4, 1, 7, 6, 9};
        System.out.println(Arrays.toString(smallestK(arr, 3)));
    }

    public static int[] smallestK(int[] arr, int k) {
        quickSort(arr, k, 0, arr.length - 1);
        int[] srr = new int[k];
        for (int i = 0; i < k; i++) {
            srr[i] = arr[i];
        }
        return srr;
    }

    static void quickSort(int[] arr, int k, int p, int r) {
        if (p >= r) {
            return;
        }
        int q = partition(arr, p, r, k);
        //此时终止递归,q前面的数字就是最小的k个数
        if (q == k) {
            return;
        }
        quickSort(arr, k, p, q - 1);
        quickSort(arr, k, q + 1, r);
    }

    static int partition(int[] arr, int p, int r, int k) {
        int pivot = arr[r];
        int i = p;
        for (int j = p; j <= r - 1; j++) {
            if (arr[j] < pivot) {
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
                i++;
            }
        }
        int temp = arr[r];
        arr[r] = arr[i];
        arr[i] = temp;
        return i;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值