【算法29天:Day29】第七章回溯算法 LeetCode 递增子序列(491)

67 阅读2分钟

题目一:

image.png

解题思路:这个题自己没写出来,但是整体思路不太行。这个题主要就是要在于递增序列以及不同的递增序列。

递增序列,不能直接对其排序,遍历的时候,如果当前元素小于path内的最后一个元素,就表明这不是一个递增序列,应当跳过。而对于不同的递增序列,其意思在于同一树层中不能存在相同元素,因此在同一层中,需要设置一个map,来记录当前元素是否已经被使用过。

回溯三部曲

  • 递归函数参数

本题求子序列,很明显一个元素不能重复使用,所以需要startIndex,调整下一层递归的起始位置。

代码如下:

let result = [];
let path [];
const backtracking(nums, startIndex)
  • 终止条件

本题其实类似求子集问题,也是要遍历树形结构找每一个节点,所以和回溯算法:求子集问题! (opens new window)一样,可以不加终止条件,startIndex每次都会加1,并不会无限递归。

但本题收集结果有所不同,题目要求递增子序列大小至少为2,所以代码如下:

if (path.length > 1) {
    result.push(path);
    // 注意这里不要加return,因为要取树上的所有节点
}
  • 单层搜索逻辑

491. 递增子序列1 在图中可以看出,同一父节点下的同层上使用过的元素就不能在使用了

那么单层搜索代码如下:

let uset = new Map(); // 使用set来对本层元素进行去重
for (let i = startIndex; i < nums.length; i++) {
    if ((!path.length && nums[i] < path[path.length - 1]) || uset.has(nums[i])){
            continue;
    }
    uset.set(nums[i], true); // 记录这个元素在本层用过了,本层后面不能再用了
    path.push(nums[i]);
    backtracking(nums, i + 1);
    path.pop();
}

对于已经习惯写回溯的同学,看到递归函数上面的uset.insert(nums[i]);,下面却没有对应的pop之类的操作,应该很不习惯吧,哈哈

这也是需要注意的点,uset; 是记录本层元素是否重复使用,新的一层uset都会重新定义(清空),所以要知道uset只负责本层!

完整代码:+100 是为了数组下标不为负数

var findSubsequences = function(nums) {
    let result = []
    let path = []
    function backtracing(startIndex) {
        if(path.length > 1) {
            result.push(path.slice())
        }
        let uset = []
        for(let i = startIndex; i < nums.length; i++) {
            if((path.length > 0 && nums[i] < path[path.length - 1]) || uset[nums[i] + 100]) {
                continue
            }
            uset[nums[i] + 100] = true
            path.push(nums[i])
            backtracing(i + 1)
            path.pop()
        }
    }
    backtracing(0)
    return result
};

// 或者使用map,使用数组效率更高

var findSubsequences = function(nums) {
    let result = []
    let path = []
    const backtracking = (nums, startIndex) => {
        if (path.length > 1) {
            result.push([...path])
        }
        let uset = new Map()
        for (let i = startIndex; i < nums.length; i++) {
            if ((path.length > 0 && nums[i] < path[path.length - 1]) || uset.has(nums[i])) {
                continue
            }
            uset.set(nums[i], true)
            path.push(nums[i])
            backtracking(nums, i + 1)
            path.pop()
        }
    }
    backtracking(nums, 0)
    return result
};