算法题 最大交换

最大交换

问题描述

给定一个非负整数,你至多可以交换一次数字中的任意两位。返回你能获得的最大值。

注意

  • 不能交换到前导零
  • 如果交换后无法获得更大值,返回原数

示例

输入: 2736
输出: 7236
解释: 交换数字2和7,得到7236。

输入: 9973
输出: 9973
解释: 无需交换,已经是最大值。

输入: 1993
输出: 9913
解释: 交换第一个1和最后一个9,得到9913。

算法思路

贪心算法

  1. 目标:找到最左边的小数字,用右边最大的数字进行交换
  2. 关键
    • 要使结果最大,优先在高位进行交换
    • 对于每个位置,寻找右边最大的数字进行交换
    • 如果有多个相同的最大数字,选择最右边的那个(为了给左边留出更大的交换空间)

代码实现

方法一:贪心 + 预处理最后位置

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. 转换为字符数组['1', '9', '9', '3']
  2. 预处理最后位置
    • lastOccurrence[1] = 0
    • lastOccurrence[9] = 2(最后一个9的位置)
    • lastOccurrence[3] = 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
    }
}

关键点

  1. 正确性

    • 高位优先:越左边的位权重越大,优先在高位进行交换
    • 最大数字:在可交换范围内选择最大的数字
    • 最右位置:如果有多个相同的最大数字,选择最右边的,避免影响更高位
  2. 预处理

    • 由于数字只有0-9,可以预处理每个数字的最后出现位置
    • 在查找时可以直接定位,无需遍历
  3. 边界情况处理

    • 单数字:无法交换,返回原数
    • 已是最大值:无需交换

常见问题

  1. 为什么选择最右边的最大数字?

    • 例如 1993,有两个9,选择最后一个9交换得到 9913,比选择第一个9交换得到 9193 更大。
  2. 为什么从9开始向下查找?

    • 确保找到的第一个可交换数字就是最大的可能值,一旦找到就可以立即返回。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值