用javascript分类刷leetcode11剪枝&回溯(图文视频讲解)

剪枝

排除那些不符合条件的分支。提高程序的运行效率。

ds_2

回溯:

一层层递归,尝试搜素答案,

  • 找到答案:返回结果,尝试其他的分支
  • 找不到答案:返回上一层,尝试其他分支

回溯模版:

result = [];
function backtrack (path, list) {
   
    if (满足条件) {
   
        result.push(path);
        return
    }

    for () {
   
        // 单层逻辑
        backtrack (path, list)
        // 撤销选择 重置状态
    }
}

回溯四部曲:

  • 回溯参数
  • 终止条件
  • 单层递归逻辑
  • 选择其他分支(撤销选择 重置状态)
473. 火柴拼正方形 (medium)

你将得到一个整数数组 matchsticks ,其中 matchsticks[i] 是第 i 个火柴棒的长度。你要用 所有的火柴棍 拼成一个正方形。你 不能折断 任何一根火柴棒,但你可以把它们连在一起,而且每根火柴棒必须 使用一次 。

如果你能使这个正方形,则返回 true ,否则返回 false 。

示例 1:

输入: matchsticks = [1,1,2,2,2]
输出: true
解释: 能拼成一个边长为2的正方形,每边两根火柴。
示例 2:

输入: matchsticks = [3,3,3,3,4]
输出: false
解释: 不能用所有火柴拼成一个正方形。

提示:

1 <= matchsticks.length <= 15
1 <= matchsticks[i] <= 108

ds_164

  • 思路 :排序nums数组,减少回溯的次数。不断尝试将nums中的元素放入4个桶中,如果都能放的下,则能拼成正方形

js:

//例子:[1,2,2,2,1]
var makesquare = function (nums) {
   
    function backtrack(i, nums, edge, bucket) {
   
        if (i >= nums.length) {
   //递归结束条件
            return true;
        }
        for (let j = 0; j < 4; j++) {
   //循环4个桶
            if (bucket[j] + nums[i] > edge) {
   //这个桶装不下 继续找下一个桶
                continue;
            }
            bucket[j] += nums[i];//将当前元素加入桶中
            if (backtrack(i + 1, nums, edge, bucket)) {
   //索引i加1 继续递归下一个nums中的元素
                return true;//下一个元素能放进桶中
            }
            bucket[j] -= nums[i];//回溯状态
        }
        return false;//循环结束都没放进合适的桶 那不能构成正方形
    }

    if (nums.length < 4) {
   //nums长度小于4 直接不能构成正方形
        return false;
    }
    let sum = 0;
    for (let i = 0; i < nums.length; i++) {
   
        sum += nums[i];
    }
    if (sum % 4) {
   //nums的和不能整除4 不能构成正方行
        return false;
    }
    nums.sort((a, b) => b - a);//排序nums
    let bucket = Array(4).fill(0);//准备4个桶
    return backtrack(0, nums, sum / 4, bucket);//传入nums元素的索引i,nums,一个边长,和桶bucket
};
52. N皇后 II(hard)

n 皇后问题 研究的是如何将 n 个皇后放置在 n × n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。

示例 1:

输入:n = 4
输出:2
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:

输入:n = 1
输出:1

提示:

1 <= n <= 9

方法1.位运算

ds_54

js:

var totalNQueens = function (n) {
   
    if (n < 1) return
    let count = 0;
    dfs(n, 0, 0, 0, 0)
    return count

      //n:皇后的数量
      //row:当前行
      //cols:放置皇后的位置
      //diag1:可以攻击的左倾斜对角线
      //diag2:可以攻击的右倾斜对角线
    function dfs(n, row, cols, diag1, diag2) {
   
        if (row >= n) {
   //递归终止 统计解法
            count += 1;
            return
        }
          //~(cols | diag1 | diag2):攻击的位置合起来 取反之后,1的位置就是可以放置皇后的位置
          //(1 << n) - 1:从右向左,大于n的位置都变成0
          //(~(cols | diag1 | diag2)) & ((1 << n) - 1):从右向左,可以放置皇后的位置,大于n的位置都变成0
        let bits = (~(cols | diag1 | diag2)) & ((1 << n) - 1)
        while (bits) {
   
            let p = bits & -bits//取到从右向左第一个1
            bits = bits & (bits - 1)//去掉从右向左第一个1
              //列和两个对角线合上不可以放置的二进制位
            dfs(n, row + 1, cols | p, (diag1 | p) << 1, (diag2 | p) >>> 1)

        }
    }
};
79. 单词搜索(medium)

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例 1:

输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
输出:true
示例 2:

输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “SEE”
输出:true
示例 3:

输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCB”
输出:false

提示:

m == board.length
n = board[i].length
1 <= m, n <= 6
1 <= word.length <= 15
board 和 word 仅由大小写英文字母组成

进阶:你可以使用搜索剪枝的技术来优化解决方案,使其在 board 更大的情况下可以更快解决问题?

ds_83

  • 思路:从上到下,左到右遍历网格,每个坐标递归调用check(i, j, k)函数,i,j表示网格坐标,k表示word的第k个字符,如果能搜索到第k个字符返回true,否则返回false,check函数的终止条件有2种情况

    1. 如果i,j位置的字符和字符串位置k的字符不相等,则这条搜索路径搜索失败 返回false
    2. 如果搜索到了字符串的结尾,则找到了网格中的一条路径,这条路径上的字符正好可以组成字符串s

    两种情况都不满足则把当前网格节点加入visited数组,visited表示节点已经访问过了,然后顺着当前网格坐标的四个方向继续尝试,如果没找到k开始的子串,则回溯状态visited[i] [j] = false,继续后面的尝试。

  • 复杂度分析:时间复杂度O(MN⋅3^L),M,N 为网格的长度与宽度,L 为字符串 word 的长度,第一次调用check函数的时候,进行4个方向的检查,其余坐标的节点都是3个方向检查,走过来的分支不会反方向回去,所以check函数的时间复杂度是3^L,而网格有M*N个坐标,且存在剪枝,所以最坏的情况下时间复杂度是O(MN⋅3^L)。空间复杂度是O(MN)visited数组空间是O(MN)check递归栈的最大深度在最坏的情况下是O(MN)

方法1:回溯

Js:

var exist = function(board, word) {
   
    const h = board.length
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值