【打卡d5】快速排序 归并排序

快速排序算法模板

------ 模板题 AcWing 785. 快速排序

void quick_sort(int q[], int l, int r)

{

if (l >= r) return;

int i = l - 1, j = r + 1, x = q[(l + r )/2];

while (i < j)

{

do i ++ ; while (q[i] < x);

do j -- ; while (q[j] > x);

if (i < j) swap(q[i], q[j]);

}

quick_sort(q, l, j), quick_sort(q, j + 1, r);

}

归并排序算法模板

------ 模板题 AcWing 787. 归并排序

void merge_sort(int q[], int l, int r)

{

if (l >= r) return;

int mid = (l + r )>> 1;

merge_sort(q, l, mid);

merge_sort(q, mid + 1, r);

int k = 0, i = l, j = mid + 1;

while (i <= mid && j <= r)

if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];

else tmp[k ++ ] = q[j ++ ];

while (i <= mid) tmp[k ++ ] = q[i ++ ];

while (j <= r) tmp[k ++ ] = q[j ++ ];

for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];

}

1. 快速排序算法(Quick Sort)

思考过程

快速排序的核心思想是 分治法 ,通过选择一个基准值(通常是数组的中间值 或者某个随机位置的值),将数组分成两部分,使得左边部分的元素小于基准值,右边部分的元素大于基准值(比较大小且交换)。然后递归地对子数组进行排序,直到整个数组有序。

具体步骤

  1. 选择基准值 :从数组中选择一个基准值,通常选择中间值 q[(l + r) / 2]
  2. 分割过程 :通过两个指针 ij,分别从左右两边向中间移动,调整元素位置swap,确保左边的元素小于基准值,右边的元素大于基准值。
  3. 交换元素:当左边的元素大于基准值且右边的元素小于基准值时,交换它们的位置。
  4. 递归调用:将分好的两个子数组递归地进行快速排序。

代码解析

复制代码
void quick_sort(int q[], int l, int r) {
    if (l >= r) return;  // 递归基准条件

    int i = l - 1, j = r + 1, x = q[(l + r) / 2];  // 基准值为中间值
    while (i < j) {
        do i++; while (q[i] < x);  // 找到左边比基准值大的数
        do j--; while (q[j] > x);  // 找到右边比基准值小的数
        if (i < j) swap(q[i], q[j]);  // 交换
    }
    quick_sort(q, l, j);  // 递归左边子数组
    quick_sort(q, j + 1, r);  // 递归右边子数组
}

关键点

  • 分治法:选择基准值后将数组分为两部分,左边小于基准,右边大于基准。
  • 双指针ij 分别从左边和右边移动,直到找到需要交换的元素。
  • 递归:对左右子数组分别进行快速排序。

2. 归并排序算法(Merge Sort)

思考过程

归并排序也是分治法的一种,通过不断地将数组分为两个子数组,直到子数组的大小为 1。然后通过 合并操作,将两个有序子数组合并成一个有序的大数组,递归地合并直到整个数组有序。

具体步骤

  1. 分割过程:将数组分成两部分,分别对每个部分进行排序。直到每个部分的大小为 1。
  2. 合并过程:将两个已经排序的子数组合并为一个有序数组。在合并过程中,利用两个指针分别指向两个子数组,选择较小的元素放入结果数组。

代码解析

复制代码
void merge_sort(int q[], int l, int r) {
    if (l >= r) return;  // 递归基准条件

    int mid = (l + r) >> 1;  // 中间位置
    merge_sort(q, l, mid);  // 递归左边
    merge_sort(q, mid + 1, r);  // 递归右边

    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)  // 合并两个有序子数组
        if (q[i] <= q[j]) tmp[k++] = q[i++];
        else tmp[k++] = q[j++];

    while (i <= mid) tmp[k++] = q[i++];  // 左边数组剩余元素
    while (j <= r) tmp[k++] = q[j++];  // 右边数组剩余元素

    for (i = l, j = 0; i <= r; i++, j++) q[i] = tmp[j];  // 将合并后的结果拷贝回原数组
}

关键点

  • 分治法:递归地将数组拆分成两部分,直到每部分只有一个元素。
  • 合并过程:合并两个已经排好序的子数组,通过比较每个子数组的最小元素,将它们合并成一个有序数组。
  • 辅助数组tmp[] 用来暂存合并后的结果。

快速排序例题

快速排序acwinghttps://www.acwing.com/problem/content/787/

1.给定你一个长度为 nn 的整数数列。

请你使用快速排序对这个数列按照从小到大进行排序。

并将排好序的数列按顺序输出。

输入格式

输入共两行,第一行包含整数 n。

第二行包含 n个整数(所有整数均在 1∼10......9范围内),表示整个数列。

输出格式

输出共一行,包含 n 个整数,表示排好序的数列。

数据范围

1≤n≤100000

输入样例:
复制代码
5
3 1 2 4 5
输出样例:
复制代码
1 2 3 4 5
cpp 复制代码
#include <iostream>
using namespace std;

int a[100000];
int n;

void quick(int a[], int l, int r) {
    if (l >= r) return;  // 递归的终止条件:当子数组只有一个或没有元素时停止

    int i = l-1, j = r+1;  // i 从 l,j 从 r 初始化
    int x = a[(l + r) / 2];  // 选择基准元素为中间元素

    while (i <= j) {  // 当 i 和 j 不交错时,继续划分
        // 向右移动 i,直到找到一个大于或等于基准的元素
        do i++; while (a[i] < x);
        // 向左移动 j,直到找到一个小于或等于基准的元素
        do j--; while (a[j] > x);

        if (i <= j) {
            // 如果 i 小于等于 j,交换 a[i] 和 a[j]
            swap(a[i], a[j]);
        }
    }

    // 递归调用对左半部分和右半部分进行排序
    quick(a, l, j);
    quick(a, i, r);
}

int main() {
    cin >> n;  // 输入数组大小
    for (int i = 0; i < n; i++) {
        cin >> a[i];  // 输入数组元素
    }

    quick(a, 0, n - 1);  // 调用快速排序

    for (int i = 0; i < n; i++) {
        cout << a[i] << " ";  // 输出排序后的数组
    }

    return 0;
}

归并排序例题

归并https://www.acwing.com/problem/content/789/

cpp 复制代码
#include <iostream>
using namespace std;

const int N = 100010;
int n;
int a[N];
int temp[N];

// 合并函数:将两个已排序的子数组合并
void merge(int a[], int l, int r) {
    if (l >= r) return;  // 递归的终止条件,当子数组只有一个元素时返回

    int mid = (l + r) / 2;  // 找到中间点

    // 递归拆分数组
    merge(a, l, mid);
    merge(a, mid + 1, r);

    int i = l, j = mid + 1, k = l;  // 初始化指针,i 和 j 分别指向左右子数组,k 用于填充 temp 数组
    while (i <= mid && j <= r) {  // 比较左右子数组的元素
        if (a[i] <= a[j]) {
            temp[k++] = a[i++];  // 如果左子数组的元素小于等于右子数组的元素,放入 temp
        } else {
            temp[k++] = a[j++];  // 否则,放入右子数组的元素
        }
    }

    // 复制左子数组剩余的元素
    while (i <= mid) {
        temp[k++] = a[i++];
    }

    // 复制右子数组剩余的元素
    while (j <= r) {
        temp[k++] = a[j++];
    }

    // 将临时数组中的元素复制回原数组
    for (int i = l; i <= r; i++) {
        a[i] = temp[i];
    }
}

int main() {
    cin >> n;  // 输入数组的大小
    for (int i = 0; i < n; i++) {
        cin >> a[i];  // 输入数组元素
    }

    merge(a, 0, n - 1);  // 调用归并排序函数

    // 输出排序后的数组
    for (int i = 0; i < n; i++) {
        cout << a[i] << " ";
    }

    return 0;
}
相关推荐
失业写写八股文1 小时前
Spring基础:Spring的事物哪些情况下会失效
java·后端·spring
程序趣谈2 小时前
算法随笔_74: 不同路径_1
数据结构·python·算法
修修修也3 小时前
算法手记3
数据结构·学习·算法·刷题
大得3694 小时前
宝塔docker切换存储目录
java·docker·eureka
L_cl4 小时前
【Python 数据结构 15.哈希表】
数据结构·算法·散列表
肖筱小瀟5 小时前
2025-3-13 leetcode刷题情况(贪心算法--区间问题)
算法·leetcode·贪心算法
东阳马生架构5 小时前
Netty基础—4.NIO的使用简介一
java·网络·netty
纽约恋情5 小时前
C++——STL 常用的排序算法
开发语言·c++·排序算法