不止数据结构——快速排序(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);
}
posted @ 2020-06-01 09:28  码匠工人  阅读(149)  评论(0编辑  收藏  举报