根据你的要求,我为你提供了LintCode“下一个最近的时间”问题的详细Java解答。这道题目的核心是找到一个距离当前时间“最近”的下一时间,要求新时间的每一位数字都必须来自原时间字符串中已有的数字。

### **暴力解法 (模拟)**
这是最直观的解法。将时间换算成分钟,然后从下一分钟开始循环,每次加1分钟,判断新时间是否由原始数字构成,直到找到第一个合法的结果。

```java
public class Solution {
    public String nextClosestTime(String time) {
        int hour = Integer.parseInt(time.substring(0, 2));
        int minute = Integer.parseInt(time.substring(3));
        // 循环寻找下一个合法时间
        while (true) {
            // 模拟时间流逝,每次增加1分钟
            if (++minute == 60) {
                minute = 0;
                if (++hour == 24) {
                    hour = 0;
                }
            }
            // 将新的时、分格式化为 HH:MM 字符串
            String newTime = String.format("%02d:%02d", hour, minute);
            // 检查新时间是否全部由原始时间的数字构成
            boolean isValid = true;
            for (int i = 0; i < newTime.length(); i++) {
                char c = newTime.charAt(i);
                if (time.indexOf(c) == -1) { // 如果原始字符串中不存在该字符
                    isValid = false;
                    break;
                }
            }
            if (isValid) {
                return newTime;
            }
        }
    }
}
```

### **深度优先搜索 (DFS) 解法**
另一种方法是枚举所有由原时间数字构成的、合法的未来时间,然后找出差值最小的那一个。

```java
public class Solution {
    // 用于存储所有可能的合法时间(以分钟计)
    private List<Integer> candidates = new ArrayList<>();
    // 存储原始时间的四个数字
    private int[] digits;

    public String nextClosestTime(String time) {
        // 1. 提取并存储原始数字
        digits = new int[]{
                time.charAt(0) - '0',
                time.charAt(1) - '0',
                time.charAt(3) - '0',
                time.charAt(4) - '0'
        };
        // 2. 将当前时间转换为分钟数
        int originalMinute = toMinute(digits[0] * 10 + digits[1], digits[2] * 10 + digits[3]);
        // 3. 通过DFS枚举所有可能的新时间组合
        dfs(0, new int[4], originalMinute);
        // 4. 找出与当前时间差值最小的未来时间
        int closestMinute = originalMinute;
        int minDiff = Integer.MAX_VALUE;
        for (int cand : candidates) {
            int diff = (cand - originalMinute + 24 * 60) % (24 * 60); // 处理跨天
            if (diff > 0 && diff < minDiff) {
                minDiff = diff;
                closestMinute = cand;
            }
        }
        // 如果没找到未来的时间,说明最近的时间是明天重复当前时间(原题要求返回自身)
        if (closestMinute == originalMinute) {
            return time;
        }
        // 将分钟数转换回“HH:MM”格式
        return String.format("%02d:%02d", closestMinute / 60, closestMinute % 60);
    }

    // DFS递归函数
    private void dfs(int index, int[] curTime, int originalMinute) {
        if (index == 4) {
            // 当构造出4个数字时,检查合法性
            int hour = curTime[0] * 10 + curTime[1];
            int minute = curTime[2] * 10 + curTime[3];
            if (hour < 24 && minute < 60) {
                int totalMinute = toMinute(hour, minute);
                candidates.add(totalMinute);
            }
            return;
        }
        // 每一位数字都可以从原始数字集中选择
        for (int digit : digits) {
            curTime[index] = digit;
            dfs(index + 1, curTime, originalMinute);
        }
    }

    // 辅助函数:将小时和分钟转换为总分钟数
    private int toMinute(int hour, int minute) {
        return hour * 60 + minute;
    }
}
```

### **算法对比**
为了帮你更好地理解,下表对比了两种主要解法的核心思路和优缺点:

| 特性 | **暴力解法 (模拟)** | **DFS解法 (枚举)** |
| :--- | :--- | :--- |
| **核心思路** | 从当前时间开始,**逐分钟向后模拟**并检查合法性。 | 枚举**所有**由给定数字构成的合法时间,再找出最近的一个。 |
| **时间复杂度** | O(1440)。最坏情况需要遍历一天1440分钟。 | O(4^4) = O(256)。枚举所有4位数字的排列组合。 |
| **空间复杂度** | O(1)。只使用常量额外空间。 | O(N)。需要存储所有合法时间的候选集。 |
| **优点** | 思路直接,代码简单,易于理解和实现。 | 当合法组合极少时,可能比暴力模拟更快。 |
| **缺点** | 如果当前时间接近午夜,合法时间在次日清晨,需要循环很多次。 | 需要处理所有组合并计算时间差,逻辑稍复杂。 |
| **代码关键点** | 使用 `String.format("%02d:%02d")` 来格式化时间,并利用 `indexOf` 检查字符是否存在。 | 使用DFS生成组合,并通过取模运算 `(t2 - t1 + 24*60) % (24*60)` 来计算时间差。 |

### **总结与建议**

- **首选暴力解法**:对于这道题,暴力解法(模拟)通常是**最佳选择**。虽然最坏需要1440次循环,但这是一个固定的常数,在算法竞赛和面试中完全可以接受,且代码简洁、逻辑清晰。
- **理解DFS思路**:DFS解法展示了另一种解决问题的视角,即通过**枚举所有可能状态**来寻找最优解,这种思路在解决其他组合类问题时很有用。
- **边界情况**:请注意,题目要求返回“下一个最近的时间”,这隐含了时间可以**跨越到第二天**。如果给定的时间数字构成的最近时间就是其自身(如“11:11”),按照题意直接返回该时间即可。

你可以先尝试理解并实现暴力解法,因为它更直观。如果遇到问题或有特殊需求(例如需要分析所有可能的组合),可以再研究DFS解法。

如果你需要我解释代码中的某一部分,或者想了解如何测试这两种解法,我可以为你提供进一步的说明。

 

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐