top1编程
← 返回题目
题解

排序

1 条题解

  • 0
    @ 2026-8-6 0:09:17

    P4635 排序(基础)

    解题思路

    这道题要把 n 个整数从小到大排序后输出,是最经典的快速排序入门题。快速排序就像给小朋友按身高排队,用「选基准、分两边」的办法一步步把队伍理顺。

    **第一步,读懂题意。**读入一个整数 n 和 n 个整数,从小到大排序后,用空格隔开输出,第一个数前面没有空格,最后要换行。

    **第二步,理解快速排序的思想。**想象一队小朋友要按身高从矮到高排。我们先从队伍中间随便拉一个人当「基准」,比如小红。然后让所有比小红矮的站到小红左边,所有比小红高的站到小红右边,这样小红的位置就固定了。接下来,左边的这一堆和右边的这一堆,各自再用同样的办法继续整理,直到每一堆都只剩一个人,整队就排好了。这种「一分为二、两边各自再来」的做法叫分治。

    **第三步,用双指针实现分组。**代码里用指针 i 从左边往右走,找到第一个「不小于基准」的数;指针 j 从右边往左走,找到第一个「不大于基准」的数。这两个数都站错了位置,把它们交换。i 和 j 继续往中间靠,重复这个过程,直到两个指针交错,一次分组就完成了。

    **第四步,递归处理左右区间。**分组完成后,基准左边的数都不大于它,右边的数都不小于它。我们再递归处理左边的区间和右边的区间。当区间里只剩一个数(left >= right)时,说明已经有序,直接返回,这就是递归的出口。

    **第五步,处理边界与细节。**分界值选中间位置的数 numbers[(left + right) / 2],不管数据有序还是乱序,划分都比较均匀。交换两个数时用一个临时变量 temp 暂存。如果所有数都相等,两个 while 循环不会移动指针,程序会通过交换让 i 和 j 继续向中间靠拢,最终也能正确结束。

    参考代码

    // 使用快速排序把整数从小到大排列并输出。
    #include <iostream>
    using namespace std;
    
    // 对区间[left,right]进行快速排序。
    void quickSort(int numbers[], int left, int right) {
        if (left >= right) return; // 区间只有一个或没有数字时已经有序。
        int i = left; // 从左边开始寻找位置不对的数字。
        int j = right; // 从右边开始寻找位置不对的数字。
        int pivot = numbers[(left + right) / 2]; // 选择中间位置的数字作为分界值。
        while (i <= j) { // 两个指针没有相遇时继续处理。
            while (numbers[i] < pivot) i++; // 左边小于分界值的数字可以保留。
            while (numbers[j] > pivot) j--; // 右边大于分界值的数字可以保留。
            if (i <= j) { // 找到一对需要交换的数字。
                int temp = numbers[i]; // 暂存左边数字。
                numbers[i] = numbers[j]; // 把右边数字放到左边。
                numbers[j] = temp; // 把左边数字放到右边。
                i++; // 左指针向右移动。
                j--; // 右指针向左移动。
            }
        }
        if (left < j) quickSort(numbers, left, j); // 排序分界值左边的区间。
        if (i < right) quickSort(numbers, i, right); // 排序分界值右边的区间。
    }
    
    int main() {
        int n; // 整数的个数。
        cin >> n; // 读入整数个数。
        int *numbers = new int[n]; // 动态申请保存整数的数组。
        int i; // 外层循环变量。
        for (i = 0; i < n; i++) { // 读入所有整数。
            cin >> numbers[i]; // 读入一个整数。
        }
        quickSort(numbers, 0, n - 1); // 对整个数组进行快速排序。
        for (i = 0; i < n; i++) { // 输出排好序的数组。
            if (i > 0) cout << ' '; // 数字之间输出一个空格。
            cout << numbers[i]; // 输出当前数字。
        }
        cout << '\n'; // 输出换行。
        delete[] numbers; // 释放申请的数组空间。
        return 0; // 程序正常结束。
    }
    

    复杂度分析

    快速排序每次把区间大致分成两半,递归深度约为 log2(n),每一层对所有元素做一次扫描,所以平均时间复杂度是 O(n log n)。在 n 为十万左右的数据量下,这个速度非常快。不过最坏情况下(比如每次基准都是最大值或最小值),划分不均匀,时间复杂度会退化为 O(n²),但题目数据通常不会刻意构造这种情况。空间上,递归调用会占用栈空间,平均深度是 O(log n),最坏是 O(n),我们用动态数组存放数据,额外空间为 O(n)。

    • 1