LeetCode 670. 最大交换
题目描述

题意分析
给定一个非负整数,最多选择两个数位交换一次,使结果尽可能大,返回这个最大整数。各个数字的数量不变,不能任意重排,也不能连续进行多次交换。
“最多一次”允许完全不交换。如果没有任何交换能让结果更大,就保留原数。题目输入不超过
10^8,数位重排后的结果仍能用 32 位有符号整数保存。
解法:记录数字最后位置的贪心交换
核心思路
[!blue]
等长数字从高位向低位比较,第一个不同位置就决定大小。因此,只要某个位置能换入右侧更大的数字,就应该优先改善最靠左的这种位置;只修改它后面的低位,收益一定更小。
确定位置后,应换入它右侧最大的数字,否则在这个最早变化的位置就输给了更大的候选。若最大的数字出现多次,应选择最右边的那个:换入的高位完全相同,但选择更右的位置,可以保留前面的较大数字,把被换走的小数字放到更低位。
数字只有
0到9,先用last[d]记录每种数字最后出现的下标,就能直接找到所需交换位置。随后从左到右枚举当前数位cur,从9向cur + 1检查;第一个满足last[d] > i的数字,就是右侧最大的候选及其最右位置。完成这一对交换后立即返回,已经同时做到“改动最早的可提升高位、换入最大数字、把损失推到最右”。若扫描结束都没有候选,任何数位右侧都没有更大的数字,当前排列已不能通过一次交换增大。
解题步骤
- 将整数转换为数位数组,从左到右更新每种数字的最后出现位置。
- 从最高位开始枚举当前位置
i和数字cur。- 依次检查比
cur大的数字,顺序从9到cur + 1。- 找到
last[digit] > i时,交换当前位置和该最后位置,转换成整数并立即返回。- 所有位置都无法改善时返回原数。
代码实现
class Solution {
public int maximumSwap(int num) {
char[] digits = String.valueOf(num).toCharArray();
int[] last = new int[10];
for (int i = 0; i < digits.length; i++) {
last[digits[i] - '0'] = i;
}
for (int i = 0; i < digits.length; i++) {
int cur = digits[i] - '0';
for (int digit = 9; digit > cur; digit--) {
// 最早能提升的高位优先,选择更大数字最右出现让换回的小值尽量靠后。
if (last[digit] > i) {
char value = digits[i];
digits[i] = digits[last[digit]];
digits[last[digit]] = value;
return Integer.parseInt(new String(digits));
}
}
}
return num;
}
}
import "strconv"
func maximumSwap(num int) int {
digits := []byte(strconv.Itoa(num))
last := make([]int, 10)
for i := 0; i < len(digits); i++ {
last[digits[i]-'0'] = i
}
for i := 0; i < len(digits); i++ {
cur := int(digits[i] - '0')
for digit := 9; digit > cur; digit-- {
// 最早能提升的高位优先,选择更大数字最右出现让换回的小值尽量靠后。
if last[digit] > i {
digits[i], digits[last[digit]] = digits[last[digit]], digits[i]
ans, _ := strconv.Atoi(string(digits))
return ans
}
}
}
return num
}
复杂度分析
设十进制数位数为 $d$。
- 时间复杂度:$O(d)$。每个位置最多检查九种候选数字,构造与解析数位也只需线性时间。
- 辅助空间复杂度:$O(d)$,用于保存数位数组;长度为十的位置表占常数空间。
关键点总结
[!green]
- 先选择最靠左的可改善位置,再选择右侧最大的数字。
- 相同候选取最右位置,才能把换回的小数字造成的影响推到最低位。
- 最后位置表一次预处理即可完成所有候选查询,不需要排序。
易错点总结
[!yellow]
- 相同大数字若取第一次出现的位置,会过早放入被换走的小数字,使结果小于选择最右位置的方案。
- 候选必须严格大于当前数字;若先交换相等数字并返回,会浪费唯一一次交换机会。
- 必须检查
last[digit] > i,确保候选在右侧;位置表默认值为零也会被这个条件自然排除。- 一旦完成交换就要返回,继续修改会违反最多交换一次的限制。
- 找不到可提升位置时无需强制交换,原数就是答案。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 31. 下一个排列 | 中等 | 原题求最接近的更大排列,本题只允许一次交换并求最大值,不能直接执行下一排列。 |
| 1323. 6 和 9 组成的最大数字 | 简单 | 同样优先改善最高位,原题只能把某个6变9,本题还要从后面选择交换来源。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!