Swift 解 LeetCode 321:拼接两个数组中的最大数,贪心 + 合并全解析


文章目录

摘要

想象你有两组数字,每组都像一个"待拼接的号码牌"。你的目标是------从中选出某几个数字,把它们拼成一个尽可能大的数。听起来是不是有点像拼接手机号,或者在广告里比大小?

这道题难点不在选数字,而在于怎么在保持原顺序的前提下拼出全场最大。我们不能随便调换顺序,那就得靠贪心 + 模拟,外加一丢丢"子问题最优合并"。

本文用 Swift 带你深入解 LeetCode 321,展示完整的解题过程与示例。

描述

给定两个数组 nums1nums2,代表两个整数的各位数字(比如 [3,4,6] 表示数字 346)。你还会得到一个整数 k,要求你从 nums1nums2 中选出 k 个数字拼成一个最大数。

注意

  • 同一数组中元素的相对顺序不能改;
  • 最终拼出的数组长度为 k
  • 你可以从任意数组中选择任意数量(只要总和为 k)的数字。

题解答案

这题的核心可以拆解成 3 个小问题:

  1. 从一个数组中取出 t 个数,使拼接后最大(经典单调栈贪心)
  2. 合并两个数组,使拼接后字典序最大(双指针比较谁大先取谁)
  3. 枚举所有 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)

总结

这题是经典的"多数组选数拼接最大"的综合题,考察点如下:

  1. 子序列最大选择技巧(单调栈)
  2. 两个数组拼最大数的合并策略(字典序比较)
  3. 枚举 + 组合尝试(全局最优)

不仅适合刷题,也非常适合作为面试题,能展示算法思维和代码组织能力。

相关推荐
isyangli_blog8 小时前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
vb2008118 小时前
FastAPI APIRouter
开发语言·python
Benszen8 小时前
KVM虚拟化解决方案
开发语言·perl
会编程的土豆8 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木8 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
杨充9 小时前
1.3 浮点型数据设计灵魂
开发语言·python·算法
噜噜噜阿鲁~9 小时前
python学习笔记 | 11.3、面向对象高级编程-多重继承
java·开发语言
basketball6169 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
春生野草9 小时前
反射、Tomcat执行
java·开发语言
_日拱一卒10 小时前
LeetCode:207课程表
java·数据结构·算法·leetcode·职场和发展