最大交换
问题描述
给定一个非负整数,你至多可以交换一次数字中的任意两位。返回你能获得的最大值。
注意:
- 不能交换到前导零
- 如果交换后无法获得更大值,返回原数
示例:
输入: 2736
输出: 7236
解释: 交换数字2和7,得到7236。
输入: 9973
输出: 9973
解释: 无需交换,已经是最大值。
输入: 1993
输出: 9913
解释: 交换第一个1和最后一个9,得到9913。
算法思路
贪心算法:
- 目标:找到最左边的小数字,用右边最大的数字进行交换
- 关键:
- 要使结果最大,优先在高位进行交换
- 对于每个位置,寻找右边最大的数字进行交换
- 如果有多个相同的最大数字,选择最右边的那个(为了给左边留出更大的交换空间)
代码实现
方法一:贪心 + 预处理最后位置
class Solution {
/**
* 通过最多一次交换获得最大数字
* 使用贪心算法:从左到右找第一个可以被更大数字替换的位置
*
* @param num 输入的非负整数
* @return 交换后能得到的最大值
*/
public int maximumSwap(int num) {
// 将数字转换为字符数组
char[] digits = String.valueOf(num).toCharArray();
int n = digits.length;
// 预处理:记录每个数字(0-9)最后出现的位置
int[] lastOccurrence = new int[10];
for (int i = 0; i < n; i++) {
lastOccurrence[digits[i] - '0'] = i;
}
// 从左到右遍历每一位
for (int i = 0; i < n; i++) {
int currentDigit = digits[i] - '0';
// 从9开始向下查找,找到第一个比当前数字大的数字
// 保证交换后结果最大
for (int maxDigit = 9; maxDigit > currentDigit; maxDigit--) {
// 检查这个更大的数字是否在当前位置右边存在
if (lastOccurrence[maxDigit] > i) {
// 执行交换
char temp = digits[i];
digits[i] = digits[lastOccurrence[maxDigit]];
digits[lastOccurrence[maxDigit]] = temp;
// 转换回整数并返回
return Integer.parseInt(new String(digits));
}
}
}
// 如果没有找到可交换的位置,返回原数字
return num;
}
}
方法二:暴力搜索
class Solution {
/**
* 尝试所有可能的交换组合
* 时间复杂度较高
*
* @param num 输入的非负整数
* @return 交换后能得到的最大值
*/
public int maximumSwap(int num) {
char[] digits = String.valueOf(num).toCharArray();
int n = digits.length;
int maxNum = num; // 记录最大值
// 尝试所有可能的交换组合
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
// 执行交换
swap(digits, i, j);
// 计算交换后的数字
int currentNum = Integer.parseInt(new String(digits));
maxNum = Math.max(maxNum, currentNum);
// 恢复原状,尝试下一种交换
swap(digits, i, j);
}
}
return maxNum;
}
/**
* 交换字符数组中两个位置的字符
*/
private void swap(char[] arr, int i, int j) {
char temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
算法分析
- 时间复杂度:
- 方法一:O(N),N是数字的位数(最多10位,实际上是O(1))
- 方法二:O(N²),暴力尝试所有交换组合
- 空间复杂度:O(N),用于存储字符数组和数组
算法过程
输入:num = 1993
- 转换为字符数组:
['1', '9', '9', '3'] - 预处理最后位置:
lastOccurrence[1] = 0lastOccurrence[9] = 2(最后一个9的位置)lastOccurrence[3] = 3
- 从左到右遍历:
- 位置0:数字1,查找9→8→…→2,找到9在位置2
- 执行交换:位置0和位置2交换
- 结果:
['9', '9', '1', '3']→9913
测试用例
public class TestMaximumSwap {
public static void main(String[] args) {
Solution solution = new Solution();
// 测试用例1:标准示例
System.out.println("Test 1: " + solution.maximumSwap(2736)); // 7236
// 测试用例2:已经是最大值
System.out.println("Test 2: " + solution.maximumSwap(9973)); // 9973
// 测试用例3:多个相同最大数字
System.out.println("Test 3: " + solution.maximumSwap(1993)); // 9913
// 测试用例4:单数字
System.out.println("Test 4: " + solution.maximumSwap(5)); // 5
// 测试用例5:两位数可交换
System.out.println("Test 5: " + solution.maximumSwap(12)); // 21
// 测试用例6:两位数不可交换
System.out.println("Test 6: " + solution.maximumSwap(21)); // 21
// 测试用例7:包含0的情况
System.out.println("Test 7: " + solution.maximumSwap(1090)); // 9010
// 测试用例8:所有数字相同
System.out.println("Test 8: " + solution.maximumSwap(1111)); // 1111
// 测试用例9:递增序列
System.out.println("Test 9: " + solution.maximumSwap(1234)); // 4231
// 测试用例10:递减序列
System.out.println("Test 10: " + solution.maximumSwap(4321)); // 4321
// 测试用例11:大数字
System.out.println("Test 11: " + solution.maximumSwap(98368)); // 98863
// 测试用例12:需要交换最后两位
System.out.println("Test 12: " + solution.maximumSwap(1203)); // 3201
}
}
关键点
-
正确性:
- 高位优先:越左边的位权重越大,优先在高位进行交换
- 最大数字:在可交换范围内选择最大的数字
- 最右位置:如果有多个相同的最大数字,选择最右边的,避免影响更高位
-
预处理:
- 由于数字只有0-9,可以预处理每个数字的最后出现位置
- 在查找时可以直接定位,无需遍历
-
边界情况处理:
- 单数字:无法交换,返回原数
- 已是最大值:无需交换
常见问题
-
为什么选择最右边的最大数字?
- 例如
1993,有两个9,选择最后一个9交换得到9913,比选择第一个9交换得到9193更大。
- 例如
-
为什么从9开始向下查找?
- 确保找到的第一个可交换数字就是最大的可能值,一旦找到就可以立即返回。
51万+

被折叠的 条评论
为什么被折叠?



