探讨快速排序(Java代码实现)

快速排序(Java代码实现)

1. 思想

​ 快速排序是一种分治(Divide and conquer)递归算法。快速排序使用分治法把一个序列分割成两个子序列,其中一部分序列均比另一部分序列小,之后再递归地分别对这两部分序列继续进行排序。

2. 算法分析

​ 快速排序是实践中的一种快速的排序算法,在对C++和Java的基本类型的排序中特别有效。它的平均时间复杂度是O(n log n),虽然它最坏情况下的时间复杂度为 O(n²),但经过一些优化可以让这种情况很难出现。

排序算法 平均时间复杂度 最好时间复杂度 最坏时间复杂度 空间复杂度 排序方式 稳定性
快速排序 O(n log n) O(n log n) O(n2) O(log n) 内部排序 不稳定

3. 算法步骤

  1. 从序列中选取一个元素作为 ”基准/枢轴“(pivot);
  2. 把序列中比基准值小的元素移动到基准左边,比基准值大的元素移动到基准的右边(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个操作称为分区/分割(partition);
  3. 递归地(recursive)把小于基准值的元素子序列和大于基准值的元素的子序列进行排序。

数据结构与算法分析(Java语言描述)书中的原话:
探讨快速排序(Java代码实现)_第1张图片

例子:
探讨快速排序(Java代码实现)_第2张图片

4. 具体步骤实现

通过前面的步骤分析我们知道第一步先选取元素作为基准,第二步再把大于基准的元素移到基准的左边,小于基准的元素移到基准的右边,那么我们在实际当中,在代码中应该怎么去实现这两步?

4.1 选取基准

怎么样选取基准?

  1. 一种错误的做法:选择固定位置作为基准

    在序列中选取最左端或最右端的元素作为基准。
    探讨快速排序(Java代码实现)_第3张图片
    Java代码实现:

//固定位置:选取序列的第一个元素作为基准
public static int SelectPivot(int array[],int low,int high)
{
     
    return array[low];
}
  1. 一种安全的做法:随机选取基准

    在序列中随机选取一个元素作为基准。
    在这里插入图片描述
    Java代码实现:

//随机选取基准:选取序列中的随机一个元素作为基准
public static int SelectPivotRandom(int array[],int low,int high){
     
    Random random = new Random();
    // nextInt(n)生成的随机数的范围不包括n,所以我们下面加1。
    int rd = random.nextInt(high - low + 1) + low;

        int temp = array[high];
        array[high] = array[rd];
        array[rd] = temp;

    return array[high];
}
  1. 一种较优的做法:三数中值法

    选取序列最左端、最右端和中间的元素的中值作为基准。
    探讨快速排序(Java代码实现)_第4张图片
    例子:

假设待排序列为:8,1, 4, 9, 6, 3, 5, 2, 7, 0,
它最最左端的元素为:8,最右端的元素为:0,中心位置 (left + right)/2 上的元素为:6。
这三个位置上的元素的中值为:6,所以选取6作为基准。

Java代码实现:

  /三值取中:选取序列中最左端,最右端,中间的元素的中值作为基准
  public static int SelectPivotMedianOfThree(int[] array, int low, int high) {
     
      //序列中间的元素的下标
      int mid = low + ((high - low) >> 1);
      //目标: array[mid] <= array[high]
      if (array[mid] > array[high])
      {
     
          int temp = array[mid];
          array[mid] = array[high];
          array[high] = temp;
      }
      //目标: array[low] <= array[high]
      if (array[low] > array[high])
      {
     
          int temp = array[low];
          array[low] = array[high];
          array[high] = temp;
      }
      //目标: array[low] >= array[mid]
      if (array[mid] > array[low])
      {
     
          int temp = array[mid];
          array[mid] = array[low];
          array[low] = temp;
      }
      //此时,array[mid] <= array[low] <= array[high]
      //low的位置上保存这三个位置中间的值
      //分割时可以直接使用low位置的元素作为枢轴,而不用改变分割函数
      return array[low];
  }

4.2 分割操作

怎么将把序列中比基准值小的元素移动到基准左边,比基准值大的元素移动到基准的右边?

双向扫描:

从右往左扫描,第一个比基准值小的元素与基准值互换;

从右往左扫描,第一个比基准值大的元素与基准值互换。

第一个比基准值小的元素的位置开始从右往左扫描,扫描到第二个比基准值小的元素与基准值互换。

第一个比基准值小的元素的位置开始从右往左扫描,扫描到第二个比基准值大的元素与基准值互换。

······

第n个比基准值小的元素的位置开始从右往左扫描,扫描到第(n+1)个比基准值小的元素与基准值互换。

第n个比基准值小的元素的位置开始从右往左扫描,扫描到第(n+1)比基准值大的元素与基准值互换。

······

直到指针low = high,完成第一次分割操作。

例子:
探讨快速排序(Java代码实现)_第5张图片

Java代码实现:

public static int[] quickSort(int array[], int low, int high) {
     
    int pivot = SelectPivotRandom(array, low, high);
    int i = low;
    int j = high;
    while (i < j) {
     
        //从右往左扫描,当元素值小于等于基准值时跳出循环
        while ((i < j) && (array[j] > pivot)) {
     
            j--;
        }
        //从左往右扫描,当元素值大于等于基准值时跳出循环
        while ((i < j) && (array[i] < pivot)) {
     
            i++;
        }
        if ((array[i] == array[j]) && (i < j)) {
     
            i++;
        } else {
     
            swap(array, i, j);
        }
    }
    if (i - 1 > low) array = quickSort(array, low, i - 1);
    if (j + 1 < high) array = quickSort(array, j + 1, high);
    return (array);
}

5.完整代码

import java.util.Random;

public class QuickSort {
     
    public static void main(String[] args) {
     
        int array[] = new int[]{
     1, 3, 7, 6, 4, 5, 0, 2, 9, 10, 8};
        int low = 0;
        int high = array.length - 1;
        array = quickSort(array, low, high);
        for (int arr : array) {
     
            System.out.print(arr + "\t");
        }
    }

    public static int[] quickSort(int array[], int low, int high) {
     
        int pivot = SelectPivotMedianOfThree(array, low, high);
        //打印
        for (int arr : array) {
     
            System.out.print(arr + "\t");
        }
        System.out.println();
        System.out.println("基准:" + pivot);
        
        int i = low;
        int j = high;
        while (i < j) {
     
            //从右往左扫描,当元素值小于等于基准值时跳出循环
            while ((i < j) && (array[j] > pivot)) {
     
                j--;
            }
            //从左往右扫描,当元素值大于等于基准值时跳出循环
            while ((i < j) && (array[i] < pivot)) {
     
                i++;
            }
            //打印
            System.out.println("交换的元素下标:" + i + "  " + j);
            
            if ((array[i] == array[j]) && (i < j)) {
     
                i++;
            } else {
     
                swap(array, i, j);
            }
        }
        if (i - 1 > low) array = quickSort(array, low, i - 1);
        if (j + 1 < high) array = quickSort(array, j + 1, high);
        return (array);
    }

    public static void swap(int[] array, int i, int j) {
     
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    //固定位置:选取序列的第一个元素作为基准
    public static int SelectPivot(int array[],int low,int high)
    {
     
        return array[low];
    }

    //随机选取基准:选取序列中的随机一个元素作为基准
    public static int SelectPivotRandom(int array[],int low,int high){
     
        Random random = new Random();
        // nextInt(n)生成的随机数的范围不包括n,所以我们下面加1。
        int rd = random.nextInt(high - low + 1) + low;

            int temp = array[high];
            array[high] = array[rd];
            array[rd] = temp;

        return array[high];
    }

    //三值取中:选取序列中最左端,最右端,中间的元素的中值作为基准
    public static int SelectPivotMedianOfThree(int[] array, int low, int high) {
     
        //序列中间的元素的下标
        int mid = low + ((high - low) >> 1);
        //目标: array[mid] <= array[high]
        if (array[mid] > array[high])
        {
     
            int temp = array[mid];
            array[mid] = array[high];
            array[high] = temp;
        }
        //目标: array[low] <= array[high]
        if (array[low] > array[high])
        {
     
            int temp = array[low];
            array[low] = array[high];
            array[high] = temp;
        }
        //目标: array[low] >= array[mid]
        if (array[mid] > array[low])
        {
     
            int temp = array[mid];
            array[mid] = array[low];
            array[low] = temp;
        }
        //此时,array[mid] <= array[low] <= array[high]
        //low的位置上保存这三个位置中间的值
        //分割时可以直接使用low位置的元素作为枢轴,而不用改变分割函数
        return array[low];
    }
}

你可能感兴趣的:(算法,java,快速排序,数据结构)