文章目录
摘要
想象你有两组数字,每组都像一个"待拼接的号码牌"。你的目标是------从中选出某几个数字,把它们拼成一个尽可能大的数。听起来是不是有点像拼接手机号,或者在广告里比大小?
这道题难点不在选数字,而在于怎么在保持原顺序的前提下拼出全场最大。我们不能随便调换顺序,那就得靠贪心 + 模拟,外加一丢丢"子问题最优合并"。
本文用 Swift 带你深入解 LeetCode 321,展示完整的解题过程与示例。

描述
给定两个数组 nums1
和 nums2
,代表两个整数的各位数字(比如 [3,4,6]
表示数字 346)。你还会得到一个整数 k
,要求你从 nums1
和 nums2
中选出 k
个数字拼成一个最大数。
注意:
- 同一数组中元素的相对顺序不能改;
- 最终拼出的数组长度为
k
; - 你可以从任意数组中选择任意数量(只要总和为
k
)的数字。
题解答案
这题的核心可以拆解成 3 个小问题:
- 从一个数组中取出
t
个数,使拼接后最大(经典单调栈贪心) - 合并两个数组,使拼接后字典序最大(双指针比较谁大先取谁)
- 枚举所有 i ∈ [0, k],分别从两个数组取 i 和 k - i 个数,试所有组合
我们要尝试所有 i
的可能组合,最后选出拼接结果最大的那一个。

题解代码分析(Swift 实现)
swift
func maxNumber(_ nums1: [Int], _ nums2: [Int], _ k: Int) -> [Int] {
// 从数组中选出 t 个数字,保持相对顺序,组成最大数
func maxSubArray(_ nums: [Int], _ t: Int) -> [Int] {
var stack = [Int]()
let drop = nums.count - t
var toDrop = drop
for num in nums {
while toDrop > 0 && !stack.isEmpty && stack.last! < num {
stack.removeLast()
toDrop -= 1
}
stack.append(num)
}
return Array(stack.prefix(t))
}
// 合并两个子数组,字典序最大
func merge(_ a: [Int], _ b: [Int]) -> [Int] {
var result = [Int]()
var i = 0, j = 0
while i < a.count || j < b.count {
if Array(a[i...]) > Array(b[j...]) {
result.append(a[i])
i += 1
} else {
result.append(b[j])
j += 1
}
}
return result
}
var best = [Int]()
let m = nums1.count, n = nums2.count
let start = max(0, k - n), end = min(k, m)
for i in start...end {
let part1 = maxSubArray(nums1, i)
let part2 = maxSubArray(nums2, k - i)
let candidate = merge(part1, part2)
if candidate > best {
best = candidate
}
}
return best
}
题解代码详解
maxSubArray ------ 单调栈选最大子序列
swift
func maxSubArray(_ nums: [Int], _ t: Int) -> [Int]
这个函数的目标是,从一个数组里选出 t
个数,保持顺序且结果最大。
思路:遍历时用一个栈维护当前选中的数,如果遇到更大的数且还可以丢掉前面的,就把前面小的弹出。经典贪心+单调栈策略。
merge ------ 合并两个数组形成最大数
swift
func merge(_ a: [Int], _ b: [Int]) -> [Int]
我们从两个数组中已经选出了部分数字,现在要合并它们,按字典序拼接成尽可能大的数组。
技巧:每次比较剩下的子数组大小,谁大谁先来。
枚举所有组合,找最大拼接
swift
for i in start...end
我们尝试在 nums1
中取 i
个,在 nums2
中取 k - i
个,只要数量合法(不超出数组长度),就尝试拼接,然后记录最大组合。
示例测试及结果
示例 1
swift
let result1 = maxNumber([3,4,6,5], [9,1,2,5,8,3], 5)
print(result1) // 输出:[9,8,6,5,3]
组合:[6,5] + [9,8,3] → merge 得 [9,8,6,5,3]
示例 2
swift
let result2 = maxNumber([6,7], [6,0,4], 5)
print(result2) // 输出:[6,7,6,0,4]
最佳组合是从两个数组各取全部拼接。
示例 3
swift
let result3 = maxNumber([3,9], [8,9], 3)
print(result3) // 输出:[9,8,9]
注意:同样的数字谁先出现会影响最终顺序,这个测试反映了 merge 时比较剩余子数组的重要性。
时间复杂度分析
- 枚举组合次数:最多 O(k)
- 每次选子序列:O(m + n)
- 合并判断:O(k)
所以总时间复杂度为:O(k × (m + n))
在题目范围(m,n ≤ 500)下是可以接受的。
空间复杂度分析
- 用到的栈、数组拼接、merge 都是 O(k) 级别
- 所以空间复杂度为:O(k)
总结
这题是经典的"多数组选数拼接最大"的综合题,考察点如下:
- 子序列最大选择技巧(单调栈)
- 两个数组拼最大数的合并策略(字典序比较)
- 枚举 + 组合尝试(全局最优)
不仅适合刷题,也非常适合作为面试题,能展示算法思维和代码组织能力。