《代码随想录》Ⅶ 回溯算法-分割 93. 复原 IP 地址
努力学习!
题目:力扣链接
-
有效 IP 地址 正好由四个整数(每个整数位于
0 到255 之间组成,且不能含有前导0),整数之间用'.' 分隔。- 例如:
"0.1.2.201" 和"192.168.1.1" 是 有效 IP 地址,但是"0.011.255.245"、"192.168.1.312" 和"192.168@1.1" 是 无效 IP 地址。
给定一个只包含数字的字符串
s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在s 中插入'.' 来形成。你 不能 重新排序或删除s 中的任何数字。你可以按 任何 顺序返回答案。 - 例如:
一、思想
这道题的核心思想是使用回溯算法来生成所有可能的IP地址组合。通过递归地尝试在每个可能的位置插入点号(.),并确保每个子串是有效的IP地址段(即0到255之间的整数,且不以0开头,除非是0本身),最终生成所有合法的IP地址。
二、代码
class Solution
{
public:
/**
* 存储所有可能的IP地址组合
*/
vector<string> res;
/**
* 存储当前的路径
*/
vector<string> path;
/**
* 判断从start到end的子串是否是一个有效的IP地址
* @param s 待判断的字符串
* @param start 开始位置
* @param end 结束位置
* @return 如果是有效的IP地址,返回true,否则返回false
*/
bool isVaild(string &s, int start, int end)
{
// 如果开始位置大于结束位置,直接返回false
if (start > end) {
return false;
}
// 如果开始位置不为0且为0,返回false
if (s[start] == '0' && start != end) {
return false;
}
int num = 0;
// 遍历子串,判断每个字符是否为数字,并计算数字的值
for (int i = start; i <= end; ++i) {
if (s[i] > '9' || s[i] < '0') {
return false;
}
num = num * 10 + s[i] - '0';
// 如果数字的值大于255,返回false
if (num > 255) {
return false;
}
}
return true;
}
/**
* 回溯算法,寻找所有可能的IP地址组合
* @param s 待恢复的字符串
* @param start 开始位置
* @param pointNum 点的数量
*/
void backtracking(string &s, int start, int pointNum)
{
// 如果点的数量为3,判断当前字符串是否是一个有效的IP地址,如果是,添加到结果中
if (pointNum == 3) {
if (isVaild(s, start, s.size() - 1)) {
res.push_back(s);
}
return;
}
// 遍历字符串,寻找有效的子串
for (int i = start; i < s.size(); i++) {
if (isVaild(s, start, i)) {
s.insert(s.begin() + i + 1, '.');
pointNum++;
backtracking(s, i + 2, pointNum);
pointNum--;
s.erase(s.begin() + i + 1);
}
else {
break;
}
}
}
/**
* 恢复IP地址
* @param s 待恢复的字符串
* @return 所有可能的IP地址组合
*/
vector<string> restoreIpAddresses(string s)
{
// 清空结果和路径
res.clear();
path.clear();
// 开始回溯
backtracking(s, 0, 0);
// 返回结果
return res;
}
};
三、代码解析
1. 算法工作原理分解
1.1 回溯函数 backtracking
-
目的:递归地生成所有可能的IP地址组合。
-
实现:
-
终止条件:
- 如果点的数量为3,说明当前字符串已经被分割成4个部分,此时判断最后一个部分是否有效。如果有效,则将当前字符串添加到结果集
res 中。
- 如果点的数量为3,说明当前字符串已经被分割成4个部分,此时判断最后一个部分是否有效。如果有效,则将当前字符串添加到结果集
-
选择与递归:
- 从
start 开始遍历字符串s,尝试将其分割成不同的子串。 - 如果当前子串
s[start:i+1] 是有效的IP地址段,则在该位置插入点号(.),并递归调用backtracking,继续处理剩余的字符串。
- 从
-
回溯:
- 在递归调用返回后,移除刚刚插入的点号,以便尝试其他可能的分割方案。
-
1.2 有效IP地址段判断函数 isVaild
-
目的:判断给定子串是否为有效的IP地址段。
-
实现:
- 检查子串是否为空、是否以0开头(除非是0本身)、是否只包含数字字符,以及是否在0到255之间。
1.3 恢复IP地址函数 restoreIpAddresses
-
目的:初始化并调用回溯函数,生成所有可能的IP地址组合。
-
实现:
- 初始化:清空结果集
res 和当前路径path。 - 调用回溯函数:从字符串的第一个字符开始,调用
backtracking 函数生成所有可能的IP地址组合。 - 返回结果:返回生成的所有IP地址组合。
- 初始化:清空结果集
2. 关键点说明
2.1 路径的选择与回溯
- 路径:
path 变量用于存储当前正在生成的IP地址组合。 - 选择:每次选择一个有效的IP地址段并将其添加到
path 中。 - 回溯:在递归调用返回后,将当前子串从
path 中移除,以便尝试其他可能的分割方案。
2.2 结果集的存储
- 结果集:
res 变量用于存储所有符合条件的IP地址组合。 - 添加条件:当
path 中的所有子串都是有效的IP地址段时,将path 添加到res 中。
2.3 剪枝优化
- 剪枝:在递归过程中,如果当前子串不是有效的IP地址段,则直接跳过,避免无效的搜索。
四、复杂度分析
-
时间复杂度:
O(3^4) 或O(1)- 由于IP地址最多有4个部分,每个部分最多有3个字符,因此时间复杂度为
O(3^4),即O(1)。
- 由于IP地址最多有4个部分,每个部分最多有3个字符,因此时间复杂度为
-
空间复杂度:
O(1)- 递归调用栈的深度最多为4,因此空间复杂度为
O(1)。此外,结果集res 的空间复杂度为O(1),因为最多只有有限数量的IP地址组合。
- 递归调用栈的深度最多为4,因此空间复杂度为
白展堂:人生就是这样,苦和累你总得选一样吧?哪有什么好事都让你一个人占了呢。 ——《武林外传》
1217

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



