Day24| 93.复原IP地址、78.子集、90.子集II
93.复原IP地址
回溯三部曲
递归参数
需要startIndex记录递归开始位置、pointNum记录添加逗号的数量
递归终止条件
要求明确分成4段,所以pointNum==3时,终止递归
终止后验证第四段是否合法,合法加入结果集
单层搜索逻辑
在循环中判断截取的子串是否合法,合法就加上
.
;不合法就结束本层循环递归调用时,下一层递归的startIndex要从i+2开始(因为需要在字符串中加入了分隔符
.
),同时记录分割符的数量pointNum 要 +1。回溯的时候,就将刚刚加入的分隔符
.
删掉就可以了,pointNum也要-1。
判断子串是否合法
段位以0为开头的数字不合法
段位里有非正整数字符不合法
段位如果大于255了不合法
public class Solution {public IList<string> res = new List<string>();public IList<string> RestoreIpAddresses(string s) {if(s.Length<4||s.Length>12) return res;Backtracking(s,0,0);return res;}public void Backtracking(string s,int startIndex,int pointNum){if(pointNum==3){if(isVaild(s,startIndex,s.Length-1)) res.Add(s);return;}for(int i=startIndex;i<s.Length;i++){if(isVaild(s,startIndex,i)){s=s.Insert(i+1,".");Backtracking(s,i+2,pointNum+1);s=s.Remove(i+1,1);}else break;}}private bool isVaild(string s,int start,int end){if(start>end) return 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';if(Num>255) return false;}return true;}
}
78.子集
子集是收集树形结构中树的所有节点的结果。
回溯三部曲
递归函数参数
path是子集收集元素、res存放结果、startIndex表示开始位置
递归终止条件
剩余集合为空时,到达叶子节点
换个角度想,不需要加终止条件,如果不满足,for循环会结束
单层搜索逻辑
不需要任何剪枝,需要遍历整棵树
public class Solution {public IList<IList<int>> res=new List<IList<int>>();public IList<int> path=new List<int>();public IList<IList<int>> Subsets(int[] nums) {BackTracking(nums,0);return res;}public void BackTracking(int[] nums,int startIndex){res.Add(new List<int>(path));if(startIndex>nums.Length) return;for(int i=startIndex;i<nums.Length;i++){path.Add(nums[i]);BackTracking(nums,i+1);path.RemoveAt(path.Count-1);}}
}
90.子集II
为了避免重复子集 —— 必须排序 + 去重
由于输入数组可能有重复元素,如 [1, 2, 2]
,我们可能会生成重复子集 [1,2]
、[1,2]
。
为了解决这个问题:
🧹 步骤一:对数组排序
csharp
Array.Sort(nums);
🧹 步骤二:在同一层中跳过重复元素
在回溯的 for 循环中,加入如下判断:
csharp
if (i > startIndex && nums[i] == nums[i - 1]) continue;
含义:
i > startIndex
:说明我们是同一层(平行节点);nums[i] == nums[i - 1]
:当前元素和前一个元素相同;所以跳过,避免重复选取开头是相同数字的路径。
去重:
Level 0 (startIndex=0): for i from 0 to 2├── i=0 (nums[0]=1) → path=[1]│ └── Level 1 (startIndex=1): for i from 1 to 2│ ├── i=1 (nums[1]=2) → path=[1,2]│ │ └── Level 2 (startIndex=2): for i from 2 to 2│ │ └── i=2 (nums[2]=2) → path=[1,2,2]│ └── i=2 (nums[2]=2) ←❗️重复元素❗️│ ❗️nums[2] == nums[1] 且 i > startIndex → 跳过├── i=1 (nums[1]=2) → path=[2]│ └── Level 1 (startIndex=2): for i from 2 to 2│ └── i=2 (nums[2]=2) → path=[2,2]├── i=2 (nums[2]=2) ←❗️重复元素❗️❗️nums[2] == nums[1] 且 i > startIndex → 跳过
public class Solution {public IList<IList<int>> res=new List<IList<int>>();public IList<int> path=new List<int>();public IList<IList<int>> SubsetsWithDup(int[] nums) {Array.Sort(nums);BackTracking(nums,0);return res;}public void BackTracking(int[] nums,int startIndex){res.Add(new List<int>(path));for(int i=startIndex;i<nums.Length;i++){if(i>startIndex&&nums[i]==nums[i-1]) continue;path.Add(nums[i]);BackTracking(nums,i+1);path.RemoveAt(path.Count-1);}}
}