一个月速刷leetcodeHOT100 day15 彻底搞懂回溯算法 以及相关题目

news/2024/7/3 0:23:03

回溯算法采用试错的思想,它尝试分步的去解决一个问题。在分步解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案

全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

**输入:**nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例 2:

**输入:**nums = [0,1]
输出:[[0,1],[1,0]]

示例 3:

**输入:**nums = [1]
输出:[[1]]

const permute = (nums) => {
// 1. 设置结果集

const result = [];

// 2. 回溯

const recursion = (path, set) => {

// 2.1 设置回溯终止条件

if (path.length === nums.length) {

// 2.1.1 推入结果集

result.push(path.concat());

// 2.1.2 终止递归

return;

}

// 2.2 遍历数组

for (let i = 0; i < nums.length; i++) {

// 2.2.1 必须是不存在 set 中的坐标

if (!set.has(i)) {

// 2.2.2 本地递归条件(用完记得删除)

path.push(nums[i]);

set.add(i);

// 2.2.3 进一步递归

recursion(path, set);

// 2.2.4 回溯:撤回 2.2.2 的操作

path.pop();

set.delete(i);

}

}

};

recursion([], new Set());

// 3. 返回结果

return result;

};

子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的
子集
(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
**输入:**nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

示例 2:

**输入:**nums = [0]
输出:[[],[0]]

var letterCombinations = (digits) => {

if (digits.length == 0) return [];

const res = [];

const map = {//建立电话号码和字母的映射关系

2: "abc",

3: "def",

4: "ghi",

5: "jkl",

6: "mno",

7: "pqrs",

8: "tuv",

9: "wxyz",

};

  

const dfs = (curStr, i) => {//curStr是递归每一层的字符串,i是扫描的指针

if (i > digits.length - 1) {//边界条件,递归的出口

res.push(curStr); //其中一个分支的解推入res

return; //结束递归分支,进入另一个分支

}

const letters = map[digits[i]]; //取出数字对应的字母

for (const l of letters) {

//进入不同字母的分支

dfs(curStr + l, i + 1); //参数传入新的字符串,i右移,继续递归

}

};

dfs("", 0); // 递归入口,传入空字符串,i初始为0的位置

return res;

};

数组总和

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:

**输入:**candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

var combinationSum = function(candidates, target) {

// 先队候选数字排序

candidates = candidates.sort((a,b)=>a-b);

const result = []; // 存储最后返回的结果

const path = []; // 路径

// sum 为当前路径上的所有元素之和

function backTrack(startIndex,sum) {

// 判断 和 是否与target相等

if( sum === target) {

result.push([...path]);

}

for(let i = startIndex; i < candidates.length; i++){

// 剪枝

if(candidates[i]+sum > target) return;

path.push(candidates[i]);

// candidates[i]表示候选数组中第i个元素的值

backTrack(i, sum + candidates[i]);

path.pop();

}

}

backTrack(0 , 0);

return result;

};

括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:

**输入:**n = 3
输出:[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”]

示例 2:

**输入:**n = 1
输出:[“()”]

var generateParenthesis = function(n) {

const res = []; // 输出的结果数组

const generate = (str, left, right) => {

if (str.length == 2 * n) { // 字符串构建完成

res.push(str); // 将字符串加入res

return; // 结束当前递归(结束当前搜索分支)

}

if (left > 0) { // 只要左括号有剩,可以选它,继续递归做选择

generate(str + '(', left - 1, right);

}

if (right > left) { // 右括号的保有数量大于左括号的保有数量,才能选右括号

generate(str + ')', left, right - 1);

}

};
  
generate('', n, n); // 递归的入口,初始字符串是空字符串,初始括号数量都是n

return res;


};

单词搜索

给定一个 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

var exist = function (board, word) {

let m = board.length;

let n = board[0].length;

//越界处理

// board[-1] = []; // 这里处理比较比较巧妙,利用了js的特性

// board.push([]);

  

//寻找首个字母

for (let x = 0; x < m; x++) {

for (let y = 0; y < n; y++) {

if (word[0] === board[x][y]) {

if (dfs(word, board, x, y, 0)) {

return true;

}

}

}

}

return false;

};

let dfs = function (word, board, x, y, index) {

let m = board.length;

let n = board[0].length;

if (index + 1 === word.length) return true;

  

var tmp = board[x][y];

// 标记该元素已使用

board[x][y] = false

if (y+1 < n && board[x][y + 1] === word[index + 1] && dfs(word, board, x, y + 1, index + 1)) return true;

if (y - 1 >= 0 && board[x][y - 1] === word[index + 1] && dfs(word, board, x, y - 1, index + 1)) return true;

if (x + 1 < m && board[x + 1][y] === word[index + 1] && dfs(word, board, x + 1, y, index + 1)) return true;

if (x - 1 >= 0 && board[x - 1][y] === word[index + 1] && dfs(word, board, x - 1, y, index + 1)) return true;

// 回溯

board[x][y] = tmp;

}

分割回文串

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是

回文串

。返回 s 所有可能的分割方案。

示例 1:

**输入:**s = “aab”
输出:[[“a”,“a”,“b”],[“aa”,“b”]]

示例 2:

**输入:**s = “a”
输出:[[“a”]]

var partition = function(s) {
    //判断回文
    const isPal = function(s, l ,r) {
        while(l < r) {
            if(s[l] !== s[r]) {
                return false;
            }
            l++;
            r--;
        }
        return true;
    }
    let res = [];
    let path = [];
    const bt = function(startIndex) {
        //如果已经切割到最后就结束
        if(startIndex === s.length) {
            res.push([...path]);
            return;
        } 
        for(let i = startIndex; i < s.length; i++) {
            // 是回文子串
            if(isPal(s, startIndex, i)) {
                path.push(s.slice(startIndex, i + 1));  //slice是右开,所以得加1
                bt(i + 1);  
                path.pop(); //回溯
            } else {
                continue;
            }
        }
    }
    bt(0);
    return res;
};

http://lihuaxi.xjx100.cn/news/2196560.html

相关文章

Java - Date类与Calendar类

在Java中&#xff0c;Date 类和 Calendar 类都被用于处理日期和时间&#xff0c;但它们之间存在一些重要的差异。下面是对这两个类的简要说明以及它们之间的主要区别。 Date 类 java.util.Date 类表示一个特定的时间点&#xff08;精确到毫秒&#xff09;。它包含自1970年1月…

【全开源】JAVA打车小程序APP打车顺风车滴滴车跑腿源码微信小程序打车源码

&#xff1a;构建便捷出行新体验 一、引言&#xff1a;探索打车系统小程序源码的重要性 在数字化快速发展的今天&#xff0c;打车系统小程序已成为我们日常生活中不可或缺的一部分。它以其便捷、高效的特点&#xff0c;极大地改变了我们的出行方式。而背后的关键&#xff0c;…

JavaScrip轮播图

前言 在网页设计中&#xff0c;轮播图&#xff08;Carousel&#xff09;已经成为一种常见的元素&#xff0c;用于展示一系列的图片或内容卡片。它们不仅能够吸引用户的注意力&#xff0c;还能节省空间&#xff0c;使得用户可以在有限的空间内获得更多的信息。今天&#xff0c;我…

【红黑树变色+旋转】

文章目录 一. 红黑树规则二. 情况一叔叔存在且为红情况二.变色旋旋 一. 红黑树规则 对于红黑树&#xff0c;进行变色旋转处理&#xff0c;终究都是为了维持颜色以下几条规则&#xff0c;只有颜色和规则维持住了&#xff0c;红黑树就维持住了最长路径的长度不超过最短路径的两倍…

LitCTF2024部分wp

litctf wp 第一次ak了web和misc&#xff0c;非常激动&#xff0c;感谢lictf给我这个机会 最终成果 全靠队里的密码逆向✌带飞。一个人就砍了近一半的分数 这里是我们队的wp web exx 题目名反过来就是xxe&#xff0c;考察xxe&#xff0c;查看登录的数据包 发现传的就是xml…

springboot配置集成RedisTemplate和Redisson,使用分布式锁案例

文章要点 自定义配置属性类集成配置RedisTemplate集成配置分布式锁Redisson使用分布式锁简单实现超卖方案 1. 项目结构 2. 集成RedisTemplate和Redisson 添加依赖 依赖的版本与继承的spring-boot-starter-parent工程相对应&#xff0c;可写可不写 <!--spring data redis…

48、Flink 的 Data Source API 详解

a&#xff09;概述 本节将描述 FLIP-27 中引入的新 Source API 的主要接口。 b&#xff09;Source Source API 是一个工厂模式的接口&#xff0c;用于创建以下组件。 Split EnumeratorSource ReaderSplit SerializerEnumerator Checkpoint Serializer 此外&#xff0c;Sou…

深度解析:短剧市场的发展趋势

一、 短剧视频的兴起 小程序短剧视频是近年来在社交媒体平台上崭露头角的一种内容形式&#xff0c;其独特的表达方式吸引了大量用户的关注&#xff0c;这种类型的视频通常以小幅度、短时长的剧情为主&#xff0c;具有轻松幽默的风格&#xff0c;适合在碎片化的时间作为娱乐消遣…