大家好,我是你的CSDN技术博主,今天是2026年1月13日,继续每日一题!
今天分享一道经典的困难 难度回溯题目------679. 24 点游戏 。这道题是面试中非常高频的"24点"变种,考察全排列 + 回溯 + 浮点精度处理,思路清晰但细节较多,强烈推荐手写几遍!
一、题目理解
题目描述
给你一个长度为 4 的整数数组 cards,每个元素在 1~9 之间。
你可以使用 +、-、×、÷ 四种运算符,对这 4 个数字进行任意顺序、任意括号组合的运算,问是否能恰好得到 24。
返回 true 或 false。
注意:
- 除法结果可以是浮点数,但最终结果必须等于 24(误差 ≤ 1e-5 视为相等)。
- 除数不能为 0。
- 运算顺序和括号可以任意组合。
示例解析
示例 1:
输入:cards = [4,1,8,7]
输出:true
解释:(8-4) × (7-1) = 4 × 6 = 24
示例 2:
输入:cards = [1,2,1,2]
输出:false
无法得到 24
示例 3:
输入:cards = [3,3,8,8]
输出:true
解释:8 ÷ (3 - 8/3) = 8 ÷ (3 - 2.666...) = 8 ÷ 0.333... = 24
二、解题思路(回溯 + 暴力枚举)
核心思想
只有 4 个数字,运算顺序和括号组合总数有限(卡特兰数级别),可以用暴力回溯枚举所有可能。
步骤:
- 每次从当前剩余数字中选 2 个数字 a、b
- 对 a、b 枚举 6 种运算(+、-×2、×、/×2)
- 将运算结果替换回列表(数字数量减 1)
- 递归处理剩余数字,直到只剩 1 个数
- 判断最后结果是否 ≈ 24(误差 ≤ 1e-6)
关键优化 & 注意点
- 除法要同时尝试 a/b 和 b/a,且除数不能为 0
- 加法和乘法满足交换律,可只试一次(但代码中为了统一,通常都写)
- 用 float 计算,避免整数除法截断
- 判断相等时用
abs(result - 24) < 1e-6或1e-9
时间复杂度:O(4! × 4^3) ≈ 几千次运算,极快
三、代码演示(Java 与 Python 双版本)
Java 版本(完整回溯)
java
import java.util.*;
class Solution {
public boolean judgePoint24(int[] cards) {
List<Double> nums = new ArrayList<>();
for (int card : cards) {
nums.add((double) card);
}
return backtrack(nums);
}
private boolean backtrack(List<Double> nums) {
if (nums.size() == 1) {
return Math.abs(nums.get(0) - 24) < 1e-6;
}
for (int i = 0; i < nums.size(); i++) {
for (int j = 0; j < nums.size(); j++) {
if (i == j) continue;
double a = nums.get(i);
double b = nums.get(j);
List<Double> newNums = new ArrayList<>();
for (int k = 0; k < nums.size(); k++) {
if (k != i && k != j) {
newNums.add(nums.get(k));
}
}
// 加法
newNums.add(a + b);
if (backtrack(newNums)) return true;
newNums.remove(newNums.size() - 1);
// 减法 a - b
newNums.add(a - b);
if (backtrack(newNums)) return true;
newNums.remove(newNums.size() - 1);
// 减法 b - a
newNums.add(b - a);
if (backtrack(newNums)) return true;
newNums.remove(newNums.size() - 1);
// 乘法
newNums.add(a * b);
if (backtrack(newNums)) return true;
newNums.remove(newNums.size() - 1);
// 除法 a / b
if (Math.abs(b) > 1e-9) {
newNums.add(a / b);
if (backtrack(newNums)) return true;
newNums.remove(newNums.size() - 1);
}
// 除法 b / a
if (Math.abs(a) > 1e-9) {
newNums.add(b / a);
if (backtrack(newNums)) return true;
newNums.remove(newNums.size() - 1);
}
}
}
return false;
}
}
Python 版本(简洁回溯)
python
from typing import List
class Solution:
def judgePoint24(self, cards: List[int]) -> bool:
def backtrack(nums: List[float]) -> bool:
if len(nums) == 1:
return abs(nums[0] - 24) < 1e-6
for i in range(len(nums)):
for j in range(len(nums)):
if i == j:
continue
a, b = nums[i], nums[j]
new_nums = [nums[k] for k in range(len(nums)) if k != i and k != j]
# 加法
if backtrack(new_nums + [a + b]):
return True
# 减法 a-b
if backtrack(new_nums + [a - b]):
return True
# 减法 b-a
if backtrack(new_nums + [b - a]):
return True
# 乘法
if backtrack(new_nums + [a * b]):
return True
# 除法 a/b
if abs(b) > 1e-9 and backtrack(new_nums + [a / b]):
return True
# 除法 b/a
if abs(a) > 1e-9 and backtrack(new_nums + [b / a]):
return True
return False
return backtrack([float(x) for x in cards])
四、代码解读
- 回溯核心:每次选两个数 → 枚举 6 种运算 → 递归处理 n-1 个数
- 浮点处理:输入转 float,除法用 float 除
- 精度判断 :
abs(result - 24) < 1e-6或1e-9 - 剪枝:除数接近 0 时跳过
五、复杂度分析
- 时间复杂度:O(4! × 4^3) ≈ 几千次运算,极快
- 空间复杂度:O(递归深度) ≈ O(4)
六、总结
这道题是暴力回溯解决排列组合问题的经典案例。核心技巧:
- 每次选 2 个数 → 枚举运算 → 缩减为 n-1 个数
- 递归到 1 个数时判断是否 ≈ 24
掌握这个模板,能轻松应对:
- 类似"四则运算得到目标值"的搜索题
- 面试中的"24点"变种
类似题目推荐:
- LeetCode 282. 给表达式添加运算符
- LeetCode 294. 翻转游戏 II
- LeetCode 464. 我能赢吗(状态压缩 + 博弈)
如果本文对你有帮助,欢迎点赞、收藏、关注~
明天每日一题再见!