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

【排序算法】 快速排序 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));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值