1. 引言

本文将探讨如何在Java中寻找由相同数字组成的下一个更大数。这个问题可以通过排列组合、排序和双指针技术解决,每种方法都有其适用场景。

2. 问题定义

给定一个正整数,要求找出使用完全相同数字组成的下一个更大数。例如:

  • 输入 123,输出应为 132
  • 输入 654444,返回 -1(表示不存在更大数)

⚠️ 关键点:必须严格使用原数字的所有数字,不能添加或删除。

3. 排列组合法

3.1 实现思路

通过生成所有数字排列组合,找出比原数大的最小值。使用TreeSet自动去重和排序:

void findPermutations(int num, int index, StringBuilder sb, Set<Integer> hs) {
    if (index == String.valueOf(num).length()) {
        hs.add(Integer.parseInt(sb.toString()));
        return;
    }
    //...
}

核心步骤

  1. 递归终止条件:当索引达到数字长度时,将当前排列加入集合
  2. 生成排列:通过交换字符位置递归生成所有排列
  3. 状态恢复:递归返回后交换回字符,确保后续排列正确
for (int i = index; i < String.valueOf(num).length(); i++) {
    char temp = sb.charAt(index);
    sb.setCharAt(index, sb.charAt(i));
    sb.setCharAt(i, temp);
    findPermutations(num, index + 1, sb, hs);
    sb.setCharAt(index, temp); // 恢复状态
    sb.setCharAt(i, sb.charAt(index));
}

主方法实现

int findNextHighestNumberUsingPermutation(int num) {
    Set<Integer> hs = new TreeSet();
    StringBuilder sb = new StringBuilder(String.valueOf(num));
    findPermutations(num, 0, sb, hs);
    for (int n : hs) {
        if (n > num) {
            return n;
        }
    }
    return -1;
}

3.2 测试验证

assertEquals(536497, findNextHighestNumberUsingPermutation(536479));
assertEquals(-1, findNextHighestNumberUsingPermutation(987654));

3.3 复杂度分析

  • 时间复杂度O(n!)(n为数字位数)
    • 生成所有排列组合是主要开销
  • 空间复杂度O(n!)
    • 最坏情况下TreeSet存储所有排列

缺点:数字位数增加时,阶乘级增长会迅速导致性能崩溃

4. 排序法

4.1 实现思路

通过数学规律和排序操作高效求解:

int findNextHighestNumberUsingSorting(int num) {
    String numStr = String.valueOf(num);
    char[] numChars = numStr.toCharArray();
    int pivotIndex;
    // ...
}

关键步骤

  1. 寻找转折点:从右向左找到第一个小于右边数字的位

    for (pivotIndex = numChars.length - 1; pivotIndex > 0; pivotIndex--) {
     if (numChars[pivotIndex] > numChars[pivotIndex - 1]) {
         break;
     }
    }
    
  2. 检查无解情况

    if (pivotIndex == 0) {
     return -1; // 数字已降序排列
    }
    
  3. 找到最小大于pivot的数字

    int pivot = numChars[pivotIndex - 1];
    int minIndex = pivotIndex;
    for (int j = pivotIndex + 1; j < numChars.length; j++) {
     if (numChars[j] > pivot && numChars[j] < numChars[minIndex]) {
         minIndex = j;
     }
    }
    
  4. 交换并排序

    swap(numChars, pivotIndex - 1, minIndex);
    Arrays.sort(numChars, pivotIndex, numChars.length);
    return Integer.parseInt(new String(numChars));
    

4.2 测试验证

assertEquals(536497, findNextHighestNumberUsingSorting(536479));
assertEquals(-1, findNextHighestNumberUsingSorting(987654));

执行流程示例(输入536479):

  1. 找到pivot=7(索引4)
  2. 在右侧找到最小大于7的数字9(索引5)
  3. 交换7和9 → 536497
  4. 对索引5后的部分排序(无需操作)

4.3 复杂度分析

  • 时间复杂度O(n log n)
    • 主要开销来自排序操作
  • 空间复杂度O(n)
    • 存储字符数组

优点:比排列组合法显著高效

5. 双指针法

5.1 实现思路

优化排序法,用双指针和反转操作替代排序:

int findNextHighestNumberUsingTwoPointer (int num) {
    String numStr = String.valueOf(num);
    char[] numChars = numStr.toCharArray();
    int pivotIndex = numChars.length - 2;
    int minIndex = numChars.length - 1;
    // ...
}

辅助方法

void swap(char[] numChars, int i, int j) {
    char temp = numChars[i];
    numChars[i] = numChars[j];
    numChars[j] = temp;
}

void reverse(char[] numChars, int i, int j) {
    while (i < j) {
        swap(numChars, i, j);
        i++;
        j--;
    }
}

核心步骤

  1. 寻找转折点

    while (pivotIndex >= 0 && numChars[pivotIndex] >= numChars[pivotIndex+1]) {
     pivotIndex--;
    }
    
  2. 检查无解情况

    if (pivotIndex == -1) {
     return -1;
    }
    
  3. 找到交换位

    while (numChars[minIndex] <= numChars[pivotIndex]) {
     minIndex--;
    }
    
  4. 交换并反转

    swap(numChars, pivotIndex, minIndex);
    reverse(numChars, pivotIndex+1, numChars.length-1);
    return Integer.parseInt(new String(numChars));
    

5.2 测试验证

assertEquals(536497, findNextHighestNumberUsingTwoPointer(536479));
assertEquals(-1, findNextHighestNumberUsingTwoPointer(987654));

执行流程示例(输入536479):

  1. pivotIndex停在4(数字7)
  2. minIndex停在5(数字9)
  3. 交换7和9 → 536497
  4. 反转索引5后的部分(单元素无需操作)

5.3 复杂度分析

  • 时间复杂度O(n)
    • 最多两次线性扫描
  • 空间复杂度O(1)
    • 仅使用常数级额外空间

优势:最优时间复杂度,适合大位数数字

6. 方法对比总结

方法 时间复杂度 空间复杂度 适用场景
排列组合法 O(n!) O(n!) 数字位数极少(n≤5)
排序法 O(n log n) O(n) 代码简洁性优先
双指针法 O(n) O(1) 大位数数字(生产环境首选)

⚠️ 踩坑提醒

  • 排列组合法在n>10时基本不可用
  • 排序法在边界条件处理容易出错
  • 双指针法需要理解反转替代排序的原理

7. 结论

本文通过三种方法解决了相同数字组成下一个更大数的问题:

  1. 排列组合法:暴力解法,仅适用于极小规模
  2. 排序法:平衡实现复杂度和性能
  3. 双指针法:最优解,时间空间复杂度均最优

推荐选择:生产环境优先使用双指针法,其线性时间复杂度和常数空间复杂度能高效处理大位数数字。对于简单场景或教学演示,排序法也是不错的选择。

本文示例代码已上传至GitHub仓库,欢迎参考实践。