算法| ss 回溯

文章详细介绍了多个编程问题,包括组合总数、全排列、子集查找、单词搜索、数字序列分析、括号生成、路径总和和火柴拼正方形的解决方案,主要使用了深度优先搜索(DFS)和回溯算法。
  • 39.组合总数
  • 46.全排列—4
  • 78.子集
  • 79.单词搜索—1
  • 连续差相同的数字—1
  • 22.括号生成
  • 113.路径总和-ii
  • 473.火柴拼正方形

39.组合总数

/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
// 思路
// dfs传参,传idx, 剩余target
// dfs返回: =0 收集, <0 false
var combinationSum = function (candidates, target) {
  const sets = [];
  const subset = [];
  dfs(0, target, subset);
  //   console.log(sets);
  return sets;
  /**
   *
   * @param {*} idx  下标开始
   * @param {*} target 剩余目标值
   * @returns
   */
  function dfs(idx, target, subset) {
    if (target < 0) return;
    if (target === 0) {
      sets.push([...subset]);
      return;
    }
    for (let j = idx; j < candidates.length; j++) {
      subset.push(candidates[j]);
      dfs(j, target - candidates[j], subset);
      subset.pop();
    }
  }
};
combinationSum([2, 3, 6, 7], 7);

46.全排列—4

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
// 思路
// 数量相等
// 剪枝 used+ i===i-1

var permuteUnique = function (nums) {
  const sets = [];
  const subset = [];
  const used = Array(nums.length).fill(0);
  dfs(subset);
  console.log(sets);
  function dfs(subset) {
    for (let i = 0; i < nums.length; i++) {
      if (subset.length === nums.length) {
        sets.push([...subset]);
        return;
      }
      if (used[i] === 1) continue;
      if (i > 0 && nums[i] === nums[i - 1] && used[i - 1] === 1) continue;
      used[i] = 1;
      subset.push(nums[i]);
      dfs(subset);
      subset.pop();
      used[i] = 0;
    }
  }
};
permuteUnique([1, 1, 2]);
// nums = [1,1,2]

78.子集

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
// 思路
// dfs idx传参是依次递增
var subsets = function (nums) {
  const sets = [];
  const subset = [];
  dfs(0, subset);
  //   console.log(sets);
  return sets;
  function dfs(idx, subset) {
    if (subset.length > nums.length) return;
    sets.push([...subset]);
    for (let i = idx; i < nums.length; i++) {
      subset.push(nums[i]);
      dfs(i + 1, subset);
      subset.pop();
    }
  }
};
subsets([1, 2, 3]);
// nums = [1,2,3]

79.单词搜索—1

/**
 * @param {character[][]} board
 * @param {string} word
 * @return {boolean}
 */
// 思路
// dfs四个方向的或值 并返回
// dfs 什么时候进入
// dfs 返回值 长度相等时
var exist = function (board, word) {
  const m = board.length;
  const n = board[0].length;
  for (let i = 0; i < m; i++) {
    for (let j = 0; j < n; j++) {
      if (board[i][j] === word[0]) {
        if (dfs(0, i, j)) return true;
      }
    }
  }
  return false;
  function dfs(idx, x, y) {
    if (x < 0 || x >= m || y < 0 || y >= n) return false;
    if (board[x][y] !== word[idx]) return false;
    if (idx === word.length - 1) return true;
    board[x][y] = null;
    const res =
      dfs(idx + 1, x + 1, y) ||
      dfs(idx + 1, x - 1, y) ||
      dfs(idx + 1, x, y + 1) ||
      dfs(idx + 1, x, y - 1);
    board[x][y] = word[idx];
    return res;
  }
};

console.log(
  exist(
    [
      ["A", "B", "C", "E"],
      ["S", "F", "C", "S"],
      ["A", "D", "E", "E"],
    ],
    "ABCCED"
  )
);
console.log(
  exist(
    [
      ["A", "B", "C", "E"],
      ["S", "F", "C", "S"],
      ["A", "D", "E", "E"],
    ],
    "ABCB"
  )
);
// board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
// [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"

连续差相同的数字—1

/**
 * @param {number} n
 * @param {number} k
 * @return {number[]}
 */
// 思路
// 进入下一轮dfs条件
// 首个或者 绝对值差为k
// dfs 返回  subset 长度等于n  并且首位不能为0
var numsSameConsecDiff = function (n, k) {
  const sets = [];
  const subset = [];
  dfs(subset);
  // console.log(sets);
  return sets;
  function dfs(subset) {
    for (let i = 0; i < 10; i++) {
      if (subset.length === n) {
        if (subset[0] !== 0) {
          sets.push(+subset.join(""));
        }
        return;
      }
      if (
        subset.length === 0 ||
        Math.abs(subset[subset.length - 1] - i) === k
      ) {
        subset.push(i);
        dfs(subset);
        subset.pop();
      }
    }
  }
};
numsSameConsecDiff(3, 7);

// 输入:n = 3, k = 7
// 输出:[181,292,707,818,929]
// 解释:注意,070 不是一个有效的数字,因为它有前导零。

22. 括号生成

/**
 * @param {number} n
 * @return {string[]}
 */

// 思路  回溯
// dfs参数  左边括号数 右边括号数  字符串
// 左边括号有剩余,继续递归
// 左边括号 数量少于右边括号 ,才能递归
// 终止条件 字符串数量等于2n
var generateParenthesis = function (n) {
  const res = [];
  dfs(n, n, "");
  console.log(res);
  return res;
  function dfs(leftRemain, rightRemain, str) {
    if (str.length === 2 * n) {
      res.push(str);
      return;
    }
    // 左边括号有剩余,继续递归
    if (leftRemain > 0) {
      dfs(leftRemain - 1, rightRemain, str + "(");
    }
    // 左边括号 数量少于右边括号 ,才能递归
    if (leftRemain < rightRemain) {
      dfs(leftRemain, rightRemain - 1, str + ")");
    }
  }
};
generateParenthesis(3);

113.路径总和-ii

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} targetSum
 * @return {number[][]}
 */
// 思路: 树+回溯的方式
// 将剩余值(目标值-当前节点的值)往下传递
// dfs什么时候要返回值,什么时候不要
// 再来看返回值,递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:

// 如果需要搜索整颗二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是本文下半部分介绍的113.路径总和ii)
// 如果需要搜索整颗二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况我们在236. 二叉树的最近公共祖先中介绍)
// 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)

var pathSum = function (root, targetSum) {
  const res = [];
  if (!root) return res;
  dfs(root, targetSum - root.val, [root.val]);
  console.log(res);
  return res;
  function dfs(node, remain, path) {
    // 终止条件
    if (remain === 0 && !node.left && !node.right) {
      res.push([...path]);
      return;
    }
    if (!node.left && !node.right) return;
    if (node.left) {
      path.push(node.left.val);
      dfs(node.left, remain - node.left.val, path);
      path.pop();
    }
    if (node.right) {
      path.push(node.right.val);
      dfs(node.right, remain - node.right.val, path);
      path.pop();
    }
  }
};
pathSum([5, 4, 8, 11, null, 13, 4, 7, 2, null, null, 5, 1], 22);
// 输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
// 输出:[[5,4,11,2],[5,8,4,5]]

473. 火柴拼正方形

/**
 * @param {number[]} matchsticks
 * @return {boolean}
 */
// 思路: 回溯
// 数组排序
// 边界判断 数组长度 数组和除4求余  数组第一个值大于边值
// 回溯算法
var makesquare = function (matchsticks) {
  if (matchsticks.length < 4) return false;
  const sum = matchsticks.reduce((x, y) => x + y, 0);
  if (sum % 4) return false;
  matchsticks.sort((a, b) => a - b);
  const side = sum / 4;
  if (matchsticks[0] > side) return false;
  let edges = [0, 0, 0, 0];
  return dfs(0);
  function dfs(idx) {
    if (idx === matchsticks.length) return true;
    for (let i = 0; i < 4; i++) {
      // 如果当前边等于上一条边了可以直接跳过,这样可以确定四条边的一致性
      if (
        edges[i] + matchsticks[idx] > side ||
        (i > 0 && edges[i] == edges[i - 1])
      )
        continue;
      edges[i] += matchsticks[idx];
      if (dfs(idx + 1)) return true;
      edges[i] -= matchsticks[idx];
    }
    return false;
  }
};
console.log(makesquare([1, 1, 2, 2, 2]));

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值