SCAU8645--归并排序(非递归算法)

8645 归并排序(非递归算法)

时间限制:1000MS  代码长度限制:10KB
提交次数:2398 通过次数:1192

题型: 编程题   语言: G++;GCC

Description

用函数实现归并排序(非递归算法),并输出每趟排序的结果



 

输入格式

第一行:键盘输入待排序关键的个数n
第二行:输入n个待排序关键字,用空格分隔数据


 

输出格式

每行输出每趟排序的结果,数据之间用一个空格分隔


 

输入样例

10
5 4 8 0 9 3 2 6 7 1


 

输出样例

4 5 0 8 3 9 2 6 1 7
0 4 5 8 2 3 6 9 1 7
0 2 3 4 5 6 8 9 1 7
0 1 2 3 4 5 6 7 8 9

#include <iostream>
#include <vector>
using namespace std;

// 合并两个有序区间
void merge(vector<int>& a, vector<int>& temp, int left, int mid, int right) {
    int i = left, j = mid, k = left;
    while (i < mid && j < right) {
        if (a[i] <= a[j]) temp[k++] = a[i++];
        else temp[k++] = a[j++];
    }
    while (i < mid) temp[k++] = a[i++];
    while (j < right) temp[k++] = a[j++];
    for (int l = left; l < right; ++l) {
        a[l] = temp[l];
    }
}

// 非递归归并排序
void mergeSort(vector<int>& a) {
    int n = a.size();
    vector<int> temp(n);
    for (int len = 1; len < n; len *= 2) {
        for (int i = 0; i < n; i += 2 * len) {
            int left = i;
            int mid = min(i + len, n);
            int right = min(i + 2 * len, n);
            merge(a, temp, left, mid, right);
        }
        // 输出当前一趟排序后的结果
        for (int i = 0; i < n; ++i) {
            cout << a[i];
            if (i != n - 1) cout << " ";
        }
        cout << endl;
    }
}

int main() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }
    mergeSort(a);
    return 0;
}

 

✅ 归并排序原理详解

归并排序(Merge Sort)是一种分治法(Divide and Conquer)思想实现的稳定排序算法,主要分为两个阶段:


🧠 核心思想:分而治之

归并排序将一个数组分成若干个子数组,每个子数组是有序的,然后再将这些有序的子数组两两合并成一个有序的大数组


🔁 工作过程(递归版):

  1. 分解(Divide)

    • 把数组不断二分,直到每个子数组只包含一个元素。

  2. 合并(Conquer)

    • 将两个有序的子数组合并成一个有序数组。

    • 一直合并直到最终合并成完整有序数组。


🔁 非递归(迭代)归并排序的流程:

非递归版本的归并排序是**自底向上(Bottom-Up)**的实现,不使用递归,而是从小段逐步向大段合并。

举个例子:

数组:[5, 4, 8, 0, 9, 3, 2, 6, 7, 1]

第一趟:每组2个元素(段长=1)
  • [5,4] → [4,5], [8,0] → [0,8],...

  • 合并后:[4,5,0,8,3,9,2,6,1,7]

第二趟:每组4个元素(段长=2)
  • [4,5,0,8] → [0,4,5,8], [3,9,2,6] → [2,3,6,9] ...

  • 合并后:[0,4,5,8,2,3,6,9,1,7]

第三趟:每组8个元素(段长=4)
  • [0,4,5,8,2,3,6,9] → [0,2,3,4,5,6,8,9]

  • 合并后:[0,2,3,4,5,6,8,9,1,7]

第四趟:整个数组(段长=8)
  • [0,2,3,4,5,6,8,9,1,7] → [0,1,2,3,4,5,6,7,8,9]


 

for (int len = 1; len < n; len *= 2) {
        for (int i = 0; i < n; i += 2 * len) {
            int left = i;
            int mid = min(i + len, n);
            int right = min(i + 2 * len, n);
            merge(a, temp, left, mid, right);
        }什么意思

 这段代码是归并排序的非递归实现中的核心部分,用于控制归并的每一“趟”,我们逐行分析它的作用。


💡 总体目标:

这段代码用来控制归并排序的分段归并过程,其中:

for (int len = 1; len < n; len *= 2)

表示:每一趟归并中,每个小段的长度是 len,并且每次翻倍(1 → 2 → 4 → 8 ...)


🔍 逐行解析:

✅ 外层循环:
for (int len = 1; len < n; len *= 2)
  • len 表示当前归并的每段的长度

  • 初始为 1,表示每个元素看作一个长度为1的有序段

  • 每次乘2,下一趟就合并更大的段


✅ 内层循环:
for (int i = 0; i < n; i += 2 * len)
  • 遍历整个数组,把每两个 len 长的段合并起来

  • i 是当前要合并的左段的起始位置

  • 步长为 2 * len,因为你一次合并两段


✅ 获取左右段的范围:
int left = i;
int mid = min(i + len, n);
int right = min(i + 2 * len, n);
  • left:当前合并段的起点(左段起点)

  • mid:右段起点(也是左段终点)

  • right:右段终点

使用 min(..., n) 是为了防止数组越界。

例如:数组长度为 10len = 4 时,

  • i = 8 时:mid = min(8 + 4, 10) = 10right = min(8 + 8, 10) = 10

  • 所以 merge(8, 10, 10),不会越界


✅ 调用归并函数:
merge(a, temp, left, mid, right);

a[left..mid-1]a[mid..right-1] 这两个有序段合并到 temp 中,再写回 a


📌 举个小例子说明:

假设数组为 [5, 4, 3, 2, 1],长度 n = 5

第1趟:len = 1(相邻两个数合并)

归并对:[5,4][3,2][1](最后剩一个不用归并)

第2趟:len = 2(两个两位段合并)

归并对:[4,5,2,3][1]

第3趟:len = 4

归并对:[2,3,4,5,1]


✅ 总结:

这段代码:

  • 实现非递归归并排序

  • 每一趟处理数组中长度为 len 的若干段落

  • 两两合并这些段

  • 最终得到一个整体有序的数组


如果你希望添加“每一趟打印当前数组”的输出,也可以放在外层 len 循环内部。需要演示我也可以加上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值