题解:洛谷 P1177 排序

【题目来源】

洛谷:P1177 【模板】排序 - 洛谷(luogu.com.cn)

【题目描述】

将读入的 \(N\) 个数从小到大排序后输出。

【输入】

第一行为一个正整数 \(N\)

第二行包含 \(N\) 个空格隔开的正整数 \(a_i\),为你需要进行排序的数。

【输出】

将给定的 \(N\) 个数从小到大输出,数之间空格隔开,行末换行且无空格。

【输入样例】

5
4 2 4 5 1

【输出样例】

1 2 4 4 5

【核心思想】

  1. 问题分析:给定 \(N\) 个正整数,要求将其从小到大排序并输出。这是一个最基础的排序问题,适合作为快速排序、堆排序或归并排序等经典算法的模板题。由于数据规模通常较大(\(N\) 可能达到 \(10^5\)),需要使用 \(O(N \log N)\) 的排序算法。

  2. 算法选择

    • 快速排序:采用分治策略,选取基准值,将小于基准的放左边,大于基准的放右边,递归排序两边。代码中选取中间或随机元素作为基准,避免最坏情况。
    • 堆排序(备选):使用小根堆,将所有元素插入堆,然后依次弹出堆顶即可得到升序序列。
    • 其他排序:归并排序、std::sort 等也可通过,但作为模板题,手写快排或堆排是学习重点。
  3. 关键步骤(以快速排序为例):

    • 读入:读取 \(N\) 和数组 \(a[0..N-1]\)
    • 分区函数:选择基准 x(如中间位置),双指针 ij 从两端向中间扫描,交换逆序对,直到 i >= j
    • 递归排序:对左半部分 [l, j] 和右半部分 [j+1, r] 分别递归排序。
    • 输出:按顺序输出数组。
  4. 时间/空间复杂度

    • 时间复杂度:平均 \(O(N \log N)\),最坏 \(O(N^2)\)(但通过随机基准可避免)。
    • 空间复杂度:\(O(\log N)\)(递归栈空间)。
  5. 排序算法的选择

    • 快速排序:在平均情况下效率最高,是常用的排序算法。
    • 堆排序:适合需要稳定输出最小值的场景,但本题只需最终有序,快排更简洁。
    • 适用场景:排序是算法竞赛中最基础的工具,掌握快速排序和堆排序是必备技能。

【算法标签】

普及- #快速排序

【代码详解】

#include <bits/stdc++.h>  // 包含标准库中的所有头文件
using namespace std;      // 使用标准命名空间

const int N = 100020;    // 定义常量N,表示数组的最大长度
int a[N];                // 定义一个全局数组a,用于存储待排序的数据

// 快速排序函数
void quick_sort(int a[], int l, int r) {
    if (l >= r) return;  // 如果左边界大于等于右边界,说明区间已经有序,直接返回

    int i = l - 1, j = r + 1;  // 初始化两个指针i和j,分别指向区间外的位置
    int x = a[l + (r - l) / 2];  // 选取区间中间位置的元素作为基准值(pivot)

    // 分区操作
    while (i < j) {
        do i++; while (a[i] < x);  // 从左向右找到第一个大于等于基准值的元素
        do j--; while (a[j] > x);  // 从右向左找到第一个小于等于基准值的元素
        if (i < j) swap(a[i], a[j]);  // 如果i<j,交换这两个元素
    }

    // 递归排序左半部分和右半部分
    quick_sort(a, l, j);      // 排序左半部分 [l, j]
    quick_sort(a, j + 1, r);  // 排序右半部分 [j+1, r]
}

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

    quick_sort(a, 0, n - 1);  // 调用快速排序函数对数组进行排序

    for (int i = 0; i < n; i++) cout << a[i] << " ";  // 输出排序后的数组
    return 0;  // 程序正常结束
}
#include <iostream>
using namespace std;

int n, a[100005];

void quicksort(int l, int r){
  if(l == r) return;  // 只有一个元素,直接返回

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

  while(i < j){
    do i++; while(a[i] < x);  // 向右找第一个>=x的元素
    do j--; while(a[j] > x);  // 向左找第一个<=x的元素

    if(i < j) swap(a[i], a[j]);  // 交换两个元素
  }

  quicksort(l, j);      // 递归排序左半部分
  quicksort(j + 1, r);  // 递归排序右半部分
}

int main(){
  cin >> n;
  for(int i = 0; i < n; i++) scanf("%d", &a[i]);
  quicksort(0, n - 1);
  for(int i = 0; i < n; i++) printf("%d ", a[i]);
  return 0;
}
// 使用acwing的模板二刷
#include <bits/stdc++.h>
using namespace std;

const int N = 100005;
int n;
int q[N];

void quick_sort(int q[], int l, int r)
{
    if (l >= r) return;  // 区间只有一个元素或为空,直接返回

    // 随机化选择基准值
    int rnd_idx = rand() % (r - l + 1) + l;
    swap(q[l], q[rnd_idx]);

    int x = q[l];  // 基准值
    int i = l - 1, j = r + 1;  // 初始化左右指针

    while (i < j)
    {
        do i++; while (q[i] < x);  // 从左向右找第一个≥x的元素
        do j--; while (q[j] > x);  // 从右向左找第一个≤x的元素

        if (i < j) swap(q[i], q[j]);  // 交换两个元素
    }

    // 递归排序左右两部分
    quick_sort(q, l, j);
    quick_sort(q, j + 1, r);
}

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> q[i];
    }

    quick_sort(q, 1, n);

    for (int i = 1; i <= n; i++)
    {
        cout << q[i] << " ";
    }
    cout << endl;

    return 0;
}
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

int a[100010], cnt;  // 堆数组, 当前堆大小

// 上浮操作:从节点u开始向上调整
void up(int u)
{
    if (u / 2 && a[u / 2] > a[u])  // 如果有父节点且父节点更大
    {
        swap(a[u], a[u / 2]);  // 交换
        up(u / 2);  // 继续向上调整
    }
}
// 下沉操作:从节点u开始向下调整
void down(int u)
{
    int v = u;  // 记录最小值的索引
    if (u * 2 <= cnt && a[u * 2] < a[v])  // 左子节点存在且更小
    {
        v = u * 2;
    }
    if (u * 2 + 1 <= cnt && a[u * 2 + 1] < a[v])  // 右子节点存在且更小
    {
        v = u * 2 + 1;
    }
    if (u != v)  // 如果u不是最小值
    {
        swap(a[u], a[v]);  // 交换
        down(v);  // 继续向下调整
    }
}
// 压入元素:在堆尾插入元素
void push(int x)
{
    a[++cnt] = x;  // 放到堆尾
    up(cnt);  // 上浮调整
}
// 弹出堆顶:删除堆顶元素
void pop()
{
    a[1] = a[cnt--];  // 用堆尾元素覆盖堆顶
    down(1);  // 下沉调整
}
int main()
{
    int n;
    scanf("%d", &n);  // 输入元素个数
    for (int i = 1, x; i <= n; i++)  // 输入每个元素
    {
        scanf("%d", &x);
        push(x);  // 建堆:从堆尾插入每个数,上浮调整
    }
    for (int i = 1; i <= n; i++)  // 堆排序:每次取出堆顶
    {
        printf("%d ", a[1]);  // 输出当前最小值
        pop();  // 删除堆顶
    }
    return 0;
}

【运行结果】

5
4 2 4 5 1
1 2 4 4 5
posted @ 2026-08-31 09:24  团爸讲算法  阅读(20)  评论(0)    收藏  举报