快速排序和归并排序

1.1.1 排序算法库函数

C++

STL:algorithm库的sort()函数 (可配合lambda表达式使用)

template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );

Java

  • 数组:Arrays.sort(array, first, last)

  • 容器:

    • Collections 类提供的Collection.sort(collection)方法
    • 自身的collection.sort(Comparator c)方法

Python

  • 列表:list.sort()方法

  • 内置函数:sorted(iterable, /, *, key=None, reverse=False)key 形参的值应该是个函数(或其他可调用对象),它接受一个参数并返回一个用于排序的键。

1.1.2 快速排序

原理

思路:

  • 选定分界点;
  • 调整序列中的元素,使当前序列最左端的元素在调整后满足左侧所有元素均小于等于分界点,右侧所有元素均大于分界点;
  • 对左右两侧递归调整,直到当前调整区间的长度不超过1。

实现:双指针思想

  • i i i j j j 双指针分别指向序列首尾
  • 只要 i i i 指向的元素小于等于分界点,就将 i i i 不断右移,直到 i i i 指向的元素大于分界点;
  • 只要 j j j 指向的元素大于分界点,就将 j j j 不断左移,直到 j j j 指向的元素小于等于分界点;
  • 交换 i i i j j j 双指针指向的元素,这样实现了一对元素的调整
  • 重复第二步和第三步,直到 i i i j j j 双指针相遇

模板

C++

注意点:因为是do while循环,先做了++ i-- j,所以int i = l - 1, j = r + 1;而且边界条件是i < j而不是i <= j

const int N = 1e6 + 10;
int nums[N], n;

void quickSort(int l, int r)
{
    if (l >= r) return;                       // 如果当前区间已经没有元素则返回
    int i = l - 1, j = r + 1, x = nums[l + r >> 1]; // 设置双指针i,j和分界点x
    while (i < j) {                           // 当i小于j时
        do ++ i ; while (nums[i] < x);        //只要i指向的元素小于等于分界点,就将i不断右移,直到i指向的元素大于分界点
        do -- j ; while (nums[j] > x);        //只要j指向的元素大于分界点,就将j不断左移,直到j指向的元素小于等于分界点
        if (i < j) swap(nums[i], nums[j]);    //交换i,j双指针指向的元素
    }
    quickSort(l, j), quickSort(j + 1, r);     // 递归排序左右区间
}

简易随机快排:

快速排序算法在序列中元素的排列比较随机时效率最高,但是当序列中的元素接近有序时,会达到最坏的时间复杂度 O ( n 2 ) O(n^2) O(n2),随机快排有两种实现方法,为把序列随机打乱和随机选择主元。

随机化序列:algorithm头文件下的[std::shuffle(first, end, g);]

#include <random>
#include <algorithm>

std::random_device rd;
std::mt19937 g(rd());

// 把数组或容器左闭右开区间[0, n)中的元素随机打乱
std::shuffle(q, q + n, g); 
quickSort(q, 0, n - 1) // 快排板子
Java
public class Main{
    
    public static void quickSort(int[] q, int l, int r) {
        if (l >= r) return;

        int i = l - 1, j = r + 1, x = q[l+r>>1];
        while (i < j) {
            do i++; while (q[i] < x);
            do j--; while (q[j] > x);
            if (i < j) {
                int temp = q[i]; q[i] = q[j]; q[j] = temp;
            }
        }

        quickSort(q, l, j);
        quickSort(q, j + 1, r);
    }
    
}
Python

Python 3.8之前:

def quickSort(nums, l, r):
    if l >= r: 
        return
    i, j, x = l - 1, r + 1, nums[l + r >> 1]
    while i < j:
        while True:
            i += 1
            if nums[i] >= x: 
                break
        while True:
            j -= 1
            if nums[j] <= x: 
                break
        if i < j: nums[i], nums[j] = nums[j], nums[i]
    quickSort(nums, l, j)
    quickSort(nums, j + 1, r)

Python 3.8之后(支持海象运算符 :=):

def quickSort(nums, l, r):
    if l >= r: 
        return
    i, j, x = l - 1, r + 1, nums[l + r >> 1]
    while i < j:
        while (i := i + 1) >= 0:
            if nums[i] >= x: 
                break
        while (j := j - 1) >= 0:
            if nums[j] <= x: 
                break
        if i < j: nums[i], nums[j] = nums[j], nums[i]
    quickSort(nums, l, j)
    quickSort(nums, j + 1, r)

1.1.3 快速选择

原理

快速选择算法解决这样一个问题:从一个无序的数组中求出第 k k k 大的数。

对区间 [ l , r ] [l, r] [l,r] 进行一次快速排序后,分界点 n u m s [ x ] nums[x] nums[x] 左侧的元素都小于等于分界点,分界点 n u m s [ x ] nums[x] nums[x] 右侧的元素都大于分界点,则分界点 n u m s [ x ] nums[x] nums[x] 就是区间 [ l , r ] [l, r] [l,r] 中第 x − l + 1 x-l+1 xl+1 大的数,因此:

  • k = x − l + 1 k=x-l+1 k=xl+1 时,第 k k k 大的数就是分界点 n u m s [ x ] nums[x] nums[x]
  • k < x − l + 1 k<x-l+1 k<xl+1 时,第 k k k 大的数在分界点 n u m s [ x ] nums[x] nums[x] 的左侧,向左侧区间递归;
  • k > x − l + 1 k>x-l+1 k>xl+1 时,第 k k k 大的数在分界点 n u m s [ x ] nums[x] nums[x] 的右侧,向右侧区间递归;
  • 因为递归边界是 l > = r l>=r l>=r ,则返回 n u m s [ l ] nums[l] nums[l]

随机选择只需要把随机快排的步骤迁移过来即可。

模板

C++
const int N = 100010;
int n, k, nums[N];

int quickSelect(int l, int r, int k) 
{
    if (l >= r) return nums[l];
    int i = l - 1, j = r + 1, x = nums[l + r >> 1];
    while (i < j) {
        do ++ i; while (nums[i] < x);
        do -- j; while (nums[j] > x);
        if (i < j)  swap(nums[i], nums[j]);
    }                                                      // 一次快排完成后,分界点x就是区间里第j-l+1大的元素
    if (k <= j - l + 1) return quickSelect(l, j, k);       // 如果k<=j-l+1,第k大的数在x左侧,向左递归
    else    return quickSelect(j + 1, r, k - (j - l + 1)); // 如果k>j-l+1,第k大的数在x右侧,向右递归
}

1.1.4 归并排序

原理

有序序列的归并:利用有序序列的单调性质,设置双指针对序列进行扫描,合并后的序列保持单调性不变。

  • i i i j j j 双指针分别指向两个递增序列 A A A B B B 的首元素,设置新的空序列 C C C
  • A [ i ] A[i] A[i] 小于等于 B [ j ] B[j] B[j] ,则 A [ i ] A[i] A[i] 是当前序列 A A A B B B 的剩余元素中最小的那个,把 A [ i ] A[i] A[i] 加入序列 C C C 中,并让 i i i 加 1;
  • A [ i ] A[i] A[i] 大于 B [ j ] B[j] B[j] ,则 B [ j ] B[j] B[j] 是当前序列 A A A B B B 的剩余元素中最小的那个,把 B [ j ] B[j] B[j] 加入序列 C C C 中,并让 j j j 加 1;
  • 重复第2,3步操作,直到双指针 i i i j j j 有任意一个达到序列末端为止,然后将另一个序列剩下的所有元素依次归入序列 C C C 中。

归并排序:反复归并序列使其最终达到有序。归并排序需要设置一个和原数组大小一样的辅助数组。

  • 把序列两两分组,将序列归并为 ⌈ n 2 ⌉ \lceil \frac{n}{2} \rceil 2n 个组,组内单独排序;
  • ⌈ n 2 ⌉ \lceil \frac{n}{2} \rceil 2n 个组两两归并为 ⌈ n 4 ⌉ \lceil \frac{n}{4} \rceil 4n 个组,组内单独排序;
  • 重复两两归并-单独排序过程,直到只剩下一个组为止

可参考:【归并排序动画演示

模板

C++
const int N = 100010;
int nums[N], tmps[N], n;

void mergeSort(int l, int r) {
    if (l >= r) return;                   // 如果当前区间已经没有元素则返回
    int mid = l + r >> 1, k = 0;
    mergeSort(l, mid), mergeSort(mid + 1, r);                  // 递归排序左右两侧
    int i = l, j = mid + 1;               // 设置双指针
    while (i <= mid && j <= r)                                 // 将左右区间有序归并
        if (nums[i] <= nums[j]) tmps[k ++] = nums[i ++];
        else     tmps[k ++] = nums[j ++];
    while (i <= mid)    tmps[k ++] = nums[i ++];               // 把左区间剩下部分归入新序列
    while (j <= r)  tmps[k ++] = nums[j ++];                   // 把右区间剩下部分归入新序列
    for (i = l, j = 0; i <= r; ++ i, ++ j)  nums[i] = tmps[j]; // 把辅助数组的值归位到原数组
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值