两个非递减有序顺序表合并成新有序顺序表(O (n+m))知识点精讲
核心考点:顺序表 + 二路归并(双指针),是数据结构线性表经典基础题。
一、基础概念
1. 顺序表
顺序表:用一段连续的内存空间存储数据元素,也就是 C 语言里的数组。
特点:
- 随机访问快:
a[i]直接取元素,(O(1)) - 插入 / 删除慢:中间增删需要移动后面大量元素,(O(n))
- 本题输入就是两个顺序表,输出也是新顺序表
非递减有序:后面元素 ≥ 前面元素,允许相等,如
1,2,2,3,5
2. 题目要求解读
时间复杂度 (O(n+m))
- n:第一个表长度,m:第二个表长度
- 含义:最多只扫描两个数组一遍,每个元素只处理一次
❌ 禁止做法:合并后再排序(复杂度 (O((n+m)\log(n+m))),不满足);逐个插入((O(nm)))
二、核心算法:二路归并(双指针法)
思想
两个已经有序的序列,同时从头部开始扫描,每次取两个指针指向元素里更小的那个放到新表。
- 指针
i:顺序表 A 当前位置,初始i=0 - 指针
j:顺序表 B 当前位置,初始j=0 - 指针
k:新顺序表存放位置,初始k=0
循环条件:i < n && j < m(两个表都还有元素没读完)
- 如果
A[i] <= B[j]:把 A [i] 放进新表,i++,k++ - 否则:把 B [j] 放进新表,
j++,k++
当其中一个表读完,剩下另一个表的剩余元素直接全部追加(本身有序,不用比较)。
复杂度分析
- 时间 (O(n+m)):i 最多走 n 次,j 最多走 m 次,所有循环加起来一共 (n+m) 次操作
- 空间 (O(n+m)):需要开辟新顺序表存放结果(题目要求生成新顺序表,不能原地覆盖原表)
二路归并也是归并排序里面最核心的子过程!归并排序就是不断拆分,最后靠二路归并合并有序子数组。
三、相关知识点拓展(容易混淆)
1. 顺序表 vs 链表的合并区别
- 本题:顺序表合并,需要开新数组存结果,连续空间。
- 如果是有序单链表合并:不需要开辟新节点,只修改指针链接,额外空间(O(1))。
2. 原地合并(倒序双指针)
如果题目改成:第一个数组 A 后面预留足够空间,直接把 B 合并进 A,不开新数组。
思路:从两个数组末尾最大值开始向前比较,大的放到 A 尾部空位。
- 时间依然 (O(n+m)),额外空间(O(1))
⚠️ 注意:本题不适用,本题明确生成一个新顺序表,所以用正向双指针即可。
3. 为什么不能直接拼接后排序?
拼接再排序虽然代码简单,但没有利用 “原数组已经有序” 这个条件,时间复杂度变成 (O((n+m)\log(n+m))),无法满足题目的复杂度要求,考试会扣分。
4. 插入法为什么不行(逐个插入)
拿一个表作为基础,另一个表元素逐个查找位置插入。
顺序表插入需要大量元素后移,最坏情况 (O(n\times m)),效率极低,不符合(O(n+m))要求。
四、考点总结(考试简答题常考)
- 顺序表的存储特点:连续内存,随机访问,插入删除代价高
- 二路归并算法原理:双指针,依次选取较小元素,剩余元素直接追加
- 复杂度计算:时间(O(n+m)),空间(O(n+m))
- 算法前提:两个输入序列本身已经有序,二路归并才能达到线性时间
- 边界情况(写代码必须考虑)
- 其中一个顺序表为空(n=0 或者 m=0),直接复制另一个表
- 存在重复元素(非递减,
<=不能写成<,否则丢元素) - 一个表全部元素都小于另一个表
边界测试案例
例 1:其中一个为空
输入:
0 3
(空行)
2 4 6
输出:长度 3,2 4 6
例 2:存在重复元素
3 3
1 2 2
2 3 4
合并结果:1 2 2 2 3 4
五、常见易错点
- 条件写成
A[i] < B[j]而不是<=→ 重复元素会丢失或者顺序错误 - 忘记处理剩余元素循环,只执行第一个 while,数组后面元素丢了
- 数组开太小,数组越界
- 复杂度分析错误,以为拼接排序是 O (n+m)
浙公网安备 33010602011771号