数据结构:数组:合并数组(Merging Arrays)

目录

第一步:明确问题本质

第二步:从最朴素的思考出发

[第三步:利用"有序性" ------ 双指针思想](#第三步:利用“有序性” —— 双指针思想)

第四步:代码实现

[🤔 为什么sizeof(A) 是整个数组的字节大小?](#🤔 为什么sizeof(A) 是整个数组的字节大小?)

第五步:复杂度分析


第一步:明确问题本质

我们的问题是:

给定两个有序数组 A 和 B,合并成一个新的有序数组 C。

输入:

  • 数组 A = [1, 3, 5](已排序)

  • 数组 B = [2, 4, 6](已排序)

目标:

  • 输出数组 C = [1, 2, 3, 4, 5, 6](也是升序排列)

第二步:从最朴素的思考出发

假设我们是个完全不懂算法的人,我们也知道一个最直接的想法:

把两个数组接在一起,然后对它排序!

思路大概是这样:

cpp 复制代码
int A[3] = {1, 3, 5};
int B[3] = {2, 4, 6};

C = A + B           // 合并:C = [1, 3, 5, 2, 4, 6]
C.sort()            // 排序:C = [1, 2, 3, 4, 5, 6]

✅ 正确,但:

  • 时间复杂度为 O((m+n) log (m+n))

  • 没有利用"原数组已排序"这一信息。

第三步:利用"有序性" ------ 双指针思想

这是我们"从零开始"的第一个优化:用两个指针分别指向两个数组的起始位置。

📍关键想法:

我们想象两个手指:

  • 一个指向 A 的当前元素(叫 i

  • 一个指向 B 的当前元素(叫 j

我们每次比较 A[i]B[j],把较小的那个放进结果数组 C,然后把对应的指针向前移动一步。

这样我们就一步步构造了有序数组 C,直到 A 和 B 的元素都被用完。

第四步:代码实现

第一步:定义数组 & 初始变量

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

int main() {
    int A[] = {1, 3, 5};
    int B[] = {2, 4, 6};

    int m = sizeof(A) / sizeof(A[0]);
    int n = sizeof(B) / sizeof(B[0]);

    int C[m + n];  // 合并后数组
  • sizeof(A) 是整个数组的字节大小

  • sizeof(A[0]) 是一个元素的字节大小

  • 所以 m 是 A 的元素个数(同理 B 的个数是 n

🤔 为什么sizeof(A) 是整个数组的字节大小?

在定义的时候:

cpp 复制代码
int A[] = {1, 2, 3};

此时 A 是一个"数组变量名",编译器知道 A 是长度为 3 的整型数组,是固定内存块,sizeof(A) 是数组大小:3 × sizeof(int)

但在其他地方,比如:

cpp 复制代码
int* ptr = A;

这里 A 的确是 数组名退化成了指针。它变成了指向第一个元素的指针 &A[0],但它本身不是个指针变量。

总结:数组和指针的差异

场景 A 是什么? sizeof(A) 返回 原因
定义处 int A[3]; 数组 整个数组字节数 编译期知道大小
函数参数 void foo(int A[]) 指针 指针大小(4/8字节) 退化成 int*
int* p = A; 指针 指针大小 A 自动退化成指针

数组名在定义时不是指针,是整个数组;在表达式中是指针,是地址。

第二步:双指针合并逻辑

cpp 复制代码
while (i < m && j < n) {
    if (A[i] < B[j]) {
        C[k] = A[i];
        i++;
    } else {
        C[k] = B[j];
        j++;
    }
    k++;
}
  • 每次比较 A[i] 和 B[j] 谁更小

  • 小的放到 C[k],移动对应指针

  • k 永远负责指向 C 的下一个空位

第三步:处理剩余元素

cpp 复制代码
while (i < m) {
    C[k] = A[i];
    i++;
    k++;
}

while (j < n) {
    C[k] = B[j];
    j++;
    k++;
}
  • 可能 A 或 B 有剩下的没合并完(另一个已经走完了)

  • 全部照顺序复制进 C

完整代码

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

int main() {
    int A[] = {1, 3, 5};
    int B[] = {2, 4, 6};
    int m = sizeof(A) / sizeof(A[0]);
    int n = sizeof(B) / sizeof(B[0]);

    int C[100];  // 假设长度不会超出 100
    int i = 0, j = 0, k = 0;

    // 双指针合并
    while (i < m && j < n) {
        if (A[i] < B[j]) {
            C[k] = A[i];
            i++;
        } else {
            C[k] = B[j];
            j++;
        }
        k++;
    }

    // 把剩余的 A 复制进去
    while (i < m) {
        C[k] = A[i];
        i++;
        k++;
    }

    // 把剩余的 B 复制进去
    while (j < n) {
        C[k] = B[j];
        j++;
        k++;
    }

    // 输出合并后的结果
    cout << "Merged Array: ";
    for (int x = 0; x < m + n; x++) {
        cout << C[x] << " ";
    }
    cout << endl;

    return 0;
}

第五步:复杂度分析

项目 分析
时间复杂度 O(m + n),每个元素只访问一次
空间复杂度 O(m + n),结果数组 C
相关推荐
东东最爱敲键盘11 分钟前
数据结构: 双向链表
数据结构
云边有个稻草人1 小时前
【C++】第二十一节—一文详解 | 红黑树实现(规则+效率+结构+插入+查找+验证)
算法·红黑树·红黑树的插入·红黑树的结构·红黑树完整代码·红黑树的效率·红黑树的查找
仟濹2 小时前
【C/C++】整数超过多少位时用「高精度」
c语言·c++·算法
DIY机器人工房6 小时前
一个可以检测本机的字节顺序,并对任意数据进行字节顺序的反转操作的代码。
嵌入式硬件·算法·嵌入式·diy机器人工房
杰克尼7 小时前
11. 盛最多水的容器
算法·leetcode·职场和发展
程序员Xu9 小时前
【OD机试题解法笔记】查找接口成功率最优时间段
笔记·算法
云泽8089 小时前
数据结构前篇 - 深入解析数据结构之复杂度
c语言·开发语言·数据结构
逝雪Yuki9 小时前
数据结构与算法——字典(前缀)树的实现
数据结构·c++·字典树·前缀树·左程云
技术思考者10 小时前
Leetcode - 反转字符串
数据结构·算法·leetcode
SKYDROID云卓小助手10 小时前
无人设备遥控器之多设备协同技术篇
网络·人工智能·嵌入式硬件·算法·信号处理