zoukankan      html  css  js  c++  java
  • 不止数据结构——快速排序(Java)

     

    快速排序

      众所周知,快速排序是一个非常常用并且效率很高的排序算法,也是面试中经常问到的算法。本文介绍了快速排序的详细过程,内容比较紧凑,同时也很直观,相信看过之后会有收获。

    算法思想

      在数组内部进行排序,每次确定一个数的位置。以递增次序为例,每次移动一个数,使得它前面的数都比他小,后面的数都比它大(为了方便理解,假设数组没有相等的元素),而前半部分和后半部分内部的次序是不确定的。该过程如图所示:

      将x移动后要保证B部分都比x小,C部分都比x大,这样就确定了x最后的位置。要完成整个数组的排序只要将B部分和C部分做同样的操作。

      一直进行下去,直到某一次B部分和C部分都只有一个元素为止,这样就完成了整个数组的排序。这是一个典型的分治思想,将整个数组的排序分解成部分排序的子问题,每次只需要找到一个元素的确定位置,再将其他的部分做同样的操作。

      那么算法的重点实际上是如何确定x的位置,使得B部分都小于x,C部分都大于x。实现的方法如下所示。

    每次将待排序数组的第一个元素设定为基准元素x。
    1. 初始化两个指针i和j,i指向第二个元素,j指向最后一个元素。i遍历过的数最后都比x小,j遍历过的数最后都比x大
    2. i向右移动直到找到一个大于x的数
    3. j向左移动直到找到一个小于x的数
    4. 交换i和j所指元素的位置(这样就保证了i遍历过的都比x小,j遍历过的都比x大)
    重复2,3,4步,直到j<i,跳出循环。
    循环结束之后将x元素和j指向的元素交换位置,这样就确定了x的位置。
    复制代码
    1. 初始化指针。
    1. i向右移动找到一个大于x的数。
    2. j向左移动找到一个小于x的数。
    1. 交换i和j指向元素的位置。交换过后的状态就还是i经过的元素都比x小,j经过的元素都比x大。
    1. 重复2,3,4步骤,直到i<j,这是后必定会停下来,因为j一旦在i的左边,j指向的元素肯定小于x,符合j停止的条件,i同理。
    1. 交换j指向元素和x的位置,这样就保证了x左边的元素都比x小,x右边的元素都比j大。

      至此,就确定了一个元素的位置。我们这里忽略掉了数组内有重复元素的情况,在编写代码时还有很多的边界情况需要考虑。

    Java实现

      这里的实现只放出了找到x位置并返回的代码,这也是快速排序最关键的部分,有了这部分代码,实现完整的排序就很简单了。这里考虑了众多边界情况,请仔细看注释。该部分建议大家可以背下来,这部分代码可以适用于很多编程题。

    // 该方法用于找到x的位置并返回
    // low,high参数表示要操作的区域(直接在待排序数组内部操作);nums参数就是整个待排序数组。
    // 返回值是x的下标,方法执行之后[low,x)部分都比x小,(x,high]部分都比x大。
    public static int partion(int low, int high, int[] nums) {
        // 如果low不比high小,则该部分不需要排序
        if (low >= high) return -1;
        // if (low > high) return -1;
        // if (low == high) return low;
        int pivot = nums[low]; // 记录x的值,这里用pivot变量表示
        int i = low; // 因为下面的循环体内是以++i开始的,所以这里i=low而不是i=low+1
        int j = high + 1; // 同理,j=high+1而不是high
        while (true) {
            // i不超过上界的前提下一直往右移动,直到找到一个数大于或者等于x,这里表示等于的情况也要交换位置。
            while (++i < high && nums[i] < pivot);
            // 与i同理
            while (--j > low && nums[j] > pivot);
            /*
            有两种情况会出现i==j,第一种是所有的元素都小于x,这样i会移动到最后,j一开始便跳出循环,这时已经可以确定x的位置就在最右边了。另一种情况是当i和j相遇在一个等于x的元素位置时,这时也确定了x的位置。所以这里设定当i>=j时就确定了x的位置应该在j处。这样的话实际上是保证了x左边的元素小于等于x,右边的大于等于x。
            */
            if (i >= j) break;
            // ij还没有相遇时交换ij所指元素的位置
           	int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
        // 确定了x的位置为j后交换元素位置
        nums[low] = nums[j];
        nums[j] = pivot;
        // 将x的位置返回
        return j;
    }
    复制代码

    完整代码

    public static int partion(int low, int high, int[] nums) {
        if (low >= high) return -1;
        int i = low;
        int j = high + 1;
        int pivot = nums[low];
        while (true) {
            while(++i < high && nums[i] < pivot);
            while(--j > low && nums[j] > pivot);
            if (j <= i) break;
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
        nums[low] = nums[j];
        nums[j] = pivot;
        return j;
    }
    // 实际的排序方法
    public static void sort(int low, int high, int[] nums) {
        int index = partion(low, high, nums);
        if (index == -1) return;
        sort(low, index - 1, nums);
        sort(index + 1, high, nums);
    }
  • 相关阅读:
    SQLSERVER查询所有数据库名,表名,和字段名
    SQL通过拆分某字段中的内容来实现与对应表连接查询
    [SPOJ]CIRU 圆并
    有关反演和GCD
    docker部署 jenkins
    mongoDB学习记录(二)
    docker动态修改容器限制
    ORACLE数据库误操作DELETE并且提交数据库之后如何恢复被删除的数据
    用8个命令调试Kubernetes集群
    db2服务器linux的cache过高原因
  • 原文地址:https://www.cnblogs.com/mczhou2/p/13023451.html
Copyright © 2011-2022 走看看