【排序算法】 快速排序 quick sort(交换类排序)

来源:互联网 发布:崩坏3矩阵buff表 编辑:程序博客网 时间:2024/06/05 01:03

【排序算法】 快速排序 quick sort(交换类排序)

在 数据结构与算法动态可视化 网站,可以观看排序时元素位置变化的过程。

快速排序的思想

从待排序的记录序列中选取一个记录(通常选取第一个记录,此处有优化空间)为枢轴,其关键字设置为 key1,然后将其余关键字小于 key1 的记录移到前面,而将关键字大于 key1 的记录移动到后面,结果将待排序记录序列分为两个子表,最后将关键字为 key1 的记录插到其分界线的位置处。将这个过程称为一趟快速排序。

通过一次划分后,就以关键字为 key1 的记录为界,将待排序序列分成了两个子表,且前面子表中所有记录的关键字均不大于key1,而后面子表中的所有记录的关键字均不小于 key1。

对分割后的子表继续按上述原则进行分割,直到所有子表的表长不超过 1 为止,此时待排序记录序列就变成了一个有序表。

快速排序的步骤

假设待划分序列为 arr[left], arr[left+1], … , arr[right],具体实现上述划分过程时,可以设置两个指针 i 和 j,它们的初始值分别为 left 和 right。

首先将基准记录 arr[left] 赋值给 key,使 arr[left],即arr[i]相当于空单元,然后反复进行如下两个扫描过程,直到 i 和 j 相遇。

a. j 从右向左扫描,直到 arr[j] < key 时,将 arr[j] 移至空单元 arr[i],此时 arr[j]相当于空单元。
b. i 从左向右扫描,直到 arr[i] > key 时,将 arr[i] 移至空单元 arr[j],此时 arr[i]相当于空单元。

当 i 和 j 相遇时,arr[i] (或 arr[j])相当于空单元,且 arr[i] 左边所有记录均不大于基准记录(切分元素),而 arr[i] 右边所有记录均不小于基准记录(切分元素)。最后,将基准记录(切分元素)移至 arr[i] 中,就完成了一次划分过程。

对于 arr[i] 左边的子表和 arr[i] 右边的子表可采用同样的方法进行进一步划分。

通俗的描述

冒泡排序,在扫描过程中只对相邻的两个元素进行比较,因此在交换两个相邻元素时只能消除一个逆序。
如果能通过两个(不相邻的)元素的交换,消除待排序数组中的多个逆序,则会加快交换排序的速度。
快速排序方法中的一次交换可能消除多个逆序。

通俗地表述:
1. 从待排序的数列中选择一个元素,作为基准元素(pivot),也成为切分元素,通常选择第一个元素或者最后一个元素
2. 对数列排序,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆放在基准后面(相等的元素可以放到任意一边)。
该基准就处于两部分的中间位置。这个称为分区(partition)操作。
3. 然后,分别对基准元素左右两部分用同样的方法继续进行排序,直到整个序列有序(递归 recursive)。

时间复杂度

expense: 时间复杂度 O(n^2),
平均时间复杂度是 O(n*log(n))

适用场景

快速排序实现

package algorithm.algorithm4.ch02_sort;import org.junit.Test;import java.util.Arrays;import java.util.Random;/** * algorithm: quick sort,快速排序 */public class Quick {    public static void sort(Comparable[] arr, int low, int high){        if (low < high){            // 调用一趟快速排序,以切分元素(枢轴元素 pivot)为界划分两个子表            int pos = partition(arr, low, high);            sort(arr, low, pos-1);  // 对左部子表快速排序            sort(arr, pos+1, high);  // 对右部子表快速排序        }    }    private static int partition(Comparable[] arr, int low, int high) {        Comparable key = arr[low];        while (low < high){            // 从右到左找一个比切分元素key小的元素            for (; low < high && arr[high].compareTo(key) >= 0; high --);            // 若找到一个比切分元素小的元素,则将它放置在低位,此时高位出生产一个空位            // 低位处的初始值是切分元素,之后低位处是一个空位            if (low < high) {                arr[low] = arr[high];                low ++;            }            // 从左到右找一个比切分元素大的元素            for (; low < high && arr[low].compareTo(key) <= 0; low ++);            // 若找到一个比切分元素大的元素,则将它放置在高位,此时低位产生一个空位            if (low < high){                arr[high] = arr[low];                high --;            }        }        arr[low] = key;  // 将切分元素放置在空位        // 为什么最后low一定是空位?        // 因为当高位没有比key小的元素时,则低位产生了空位        // 或者低位没有比key大的元素时,高位产生了空位,则while(low < high)一直执行到low == high        return low;    }    @Test    public void test(){        Random random = new Random();        int N = 20;        Integer[] arr = new Integer[N];        for (int i = 0; i < N; i++) arr[i] = random.nextInt(100);        System.out.println(Arrays.toString(arr));        Quick.sort(arr, 0, arr.length-1);        System.out.println(Arrays.toString(arr));    }}