上一篇:回溯算法的组合问题
这一篇记录回溯算法的切割问题与子集问题。
1.分割回文串
131. 分割回文串-中等
b站视频讲解
难点:
- 切割问题如何抽象为组合问题?
- 用什么模拟切割线?
- 递归终止条件
function isPalindrome(s, l, r) {
while(l <= r) {
if(s[l] !== s[r]) {
return false;
}
l++, r--;
}
return true;
}
var partition = function(s) {
const ans = [], path = [];
const backtracing = (s, index) => {
if(index >= s.length) {
ans.push([...path]);
return;
}
for(let i = index; i < s.length; i++) {
if(isPalindrome(s, index, i)) {
let str = s.substring(index, i + 1);
path.push(str);
}else{
continue;
}
backtracing(s, i + 1);
path.pop();
}
}
backtracing(s, 0);
return ans;
};
2.复原 IP 地址
93. 复原 IP 地址-中等
b站视频讲解
思路:利用.
作分割,一个合法的IP地址总共有三个.
,每次都需要对.
之前的子串进行判断,看是否合法。当有三个.
时,需要对.
之后的子串进行判断。
var restoreIpAddresses = function(s) {
const ans = [], path = [];
const backtracing = (index) => {
if(path.length > 4) {
return;
}
if(path.length === 4 && index === s.length) {
ans.push(path.join('.'));
return;
}
for(let i = index; i < s.length; i++) {
let number = s.substring(index, i + 1);
if(number.length > 3 || +number > 255) break;
if(number.length > 1 && number[0] === '0') break;
path.push(number);
backtracing(i + 1);
path.pop();
}
}
backtracing(0);
return ans;
};
3.子集
78. 子集-中等
b站视频讲解
思路:子集问题与组合、切割问题不同之处在于,子集问题要收集每个节点,而组合、切割问题只收集叶子节点。做完了组合与切割问题的题,一下子就能写出来子集的题。
var subsets = function(nums) {
const ans = [], path = [];
const backtracing = (index) => {
ans.push([...path]);
for(let i = index; i < nums.length; i++) {
path.push(nums[i]);
backtracing(i + 1);
path.pop();
}
}
backtracing(0);
return ans;
};
4.子集 II
90. 子集 II-中等
b站视频讲解
思路:该题与组合总和II很像,在上题(子集)的基础上加了去重。
var subsetsWithDup = function(nums) {
const ans = [], path = [];
nums.sort();
const backtracing = (index) => {
ans.push([...path]);
for(let i = index; i < nums.length; i++) {
if(i > index && nums[i] === nums[i - 1]) continue;
path.push(nums[i]);
backtracing(i + 1);
path.pop();
}
}
backtracing(0);
return ans;
};
5. 递增子序列
491. 递增子序列-中等
b站视频讲解
重点:
- 怎么使其递增
本题不是比较nums[i]
与nums[i-1]
的关系,而是比较nums[i]
与path[path.length-1]
的关系。 - 怎么去重
与前几道题不同的是,该题中的数组不能排序,那么怎么去重呢?使用Set
或者uset
数组,每一层定义一个uset
,进行判断。
- 注:for循环内的if条件判断的与/或关系很重要。
if(path.length > 1) { ans.push([...path]); // return; }
中不要写return
,因为把path
放进ans
后,还要接着向下做判断,把符合条件的数组元素继续放进path
里。否则path
里会只有两个元素。
var findSubsequences = function(nums) {
const ans = [], path = [];
const backtracing = (index) => {
if(path.length > 1) {
ans.push([...path]);
}
// let set = new Set();
let uset = [];
for(let i = index; i < nums.length; i++) {
if((path.length > 0 && nums[i] < path[path.length - 1]) || uset[nums[i] + 100]) {
// set.has(nums[i])
continue;
}
// set.add(nums[i]);
uset[nums[i] + 100] = true;
path.push(nums[i]);
backtracing(i + 1);
path.pop();
}
}
backtracing(0);
return ans;
};