hot100 -- 回溯(下)

2024-06-02 04:44
文章标签 回溯 hot100

本文主要是介绍hot100 -- 回溯(下),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

👂 ​​​​​​​▶ 幸福就是 (163.com)

👂 ▶ 当爱在靠近 (163.com)

目录

🚩括号生成

AC  DFS

🌼单词搜索

AC  DFS

🎂分割回文串

AC  DFS+DP

AC  DFS+记忆化

🌼N 皇后

AC  DFS


🚩括号生成

22. 括号生成 - 力扣(LeetCode)

AC  DFS

class Solution {
private:string temp; // 临时答案vector<string> ans;void dfs(int n, int l, int r) { // 左括号数 l, 右括号数 r// 递归出口if (temp.size() == 2*n) {ans.push_back(temp);return;}// 递归if (l < n) {temp.push_back('(');dfs(n, l + 1, r);temp.pop_back(); // 回溯时取消标记}if (r < l) { // 为了保证匹配,这里 r < l,保证左边的 '(' 匹配 右边的 ')'temp.push_back(')');dfs(n, l, r + 1);temp.pop_back();}}
public:vector<string> generateParenthesis(int n) {dfs(n, 0, 0);return ans;}
};

🌼单词搜索

79. 单词搜索 - 力扣(LeetCode)

AC  DFS

每个点 DFS 一次,有任一路径满足即可

class Solution {
private:int nex[4][2] = {{1,0},{-1,0},{0,1},{0,-1}}; // 4 个方向int vis[8][8];// (x, y) 开始, word 中第 count 个字符bool dfs(int x, int y, int count, vector<vector<char>>& board, string word) {int num = word.size(), m = board.size(), n = board[0].size();// 递归出口if (board[x][y] != word[count])return false;if (count == num - 1) return true;vis[x][y] = 1; // 标记int result = 0;// 递归for (int i = 0; i < 4; ++i) {int xx = x + nex[i][0], yy = y + nex[i][1];if (xx >= 0 && xx < m && yy >= 0 && yy < n && !vis[xx][yy]) { // 未越界 && 未访问int flag = dfs(xx, yy, count + 1, board, word); // 递归   if (flag) {result = 1;break;}}}vis[x][y] = 0; // 取消标记return result;}
public:bool exist(vector<vector<char>>& board, string word) {int m = board.size(), n = board[0].size();// 从每一个点开始 DFSfor (int i = 0; i < m; ++i) for (int j = 0; j < n; ++j) {int flag = dfs(i, j, 0, board, word);if (flag) return true;}return false;}
};

🎂分割回文串

131. 分割回文串 - 力扣(LeetCode)

AC  DFS+DP

1)常规判断回文,用双指针 O(n),这里采用 DP O(1)👇

a. 含义:dp[i][j] == 1,字符串片段 [i, j] 是回文串

b. 递推式:dp[i][j] = s[i] == s[j] && dp[i + 1][j - 1]

c. 初始化:全部初始化为 1,两层 for 循环中,加个 s[i] == s[j] 的条件即可

d. 遍历顺序:根据递推式,且考虑到 i <= j,i 由 i + 1 得到,i 从 i = n - 1 开始遍历,j 从 i 开始

2)注意递归 dfs(s, j + 1),而不是 dfs(s, x + 1),因为当前 [x, j] 部分已经分割出来,并插入 temp 了,下一部分应该从 j + 1 开始 

 3)坑:第 33 行,dp 递推那里,j = i + 1 开始,否则当 i = 0, j = i = 0,此时 j - 1 == -1,会出现 dp[1][-1] 导致溢出

时间 O(n * 2^n)

O(n) 字符串长度

O(2^n) 长度为 n 的字符串划分方案数 2^(n-1) ≈ O(2^n)

class Solution {
private:vector<string> temp; // 临时答案vector<vector<string>> ans;vector<vector<int>> dp;int n; // 字符串 s 长度// 分割字串 [x, ...]void dfs(string s, int x) {// 递归出口:字符串末尾, 分割完毕if (x == n) {ans.push_back(temp);return;}// [x, x] ... [x, n-1]for (int j = x; j < n; ++j) {if (dp[x][j]) { // 回文temp.push_back(s.substr(x, j - x + 1));// [x, j] -> [j + 1, ...]// 递归分割下一部分dfs(s, j + 1);temp.pop_back(); // 回溯 恢复现场}}}
public:vector<vector<string>> partition(string s) {n = s.size();// 初始化 dp 为 1dp.resize(n, vector<int>(n, 1)); // 等价于 assign// dp 递归式for (int i = n - 1; i >= 0; --i)for (int j = i + 1; j < n; ++j) // j = i + 1 开始,防止 i = 0 时 溢出dp[i][j] = (s[i] == s[j]) && dp[i + 1][j - 1];dfs(s, 0); // 下标 0 开始分割 [0, ...]return ans; }
};

AC  DFS+记忆化

将上面 O(1) 的 dp 变成 DFS 中的记忆化,本质一样,都是通过记录已有的结果,减少重复计算

记忆化一般用递归 return 实现

比 DP O(1) 求回文,慢很多

class Solution {
private:vector<string> temp; // 临时答案vector<vector<string>> ans;vector<vector<int>> dp;int n; // 字符串 s 长度// 分割字串 [x, ...]void dfs(string s, int x) {// 递归出口:字符串末尾, 分割完毕if (x == n) {ans.push_back(temp);return;}// [x, x] ... [x, n-1]for (int j = x; j < n; ++j) {if (isParlin(x, j, s) == 1) { // 回文temp.push_back(s.substr(x, j - x + 1));// [x, j] -> [j + 1, ...]// 递归分割下一部分dfs(s, j + 1);temp.pop_back(); // 回溯 恢复现场}}}// 记忆化搜索判断回文// 0 未搜索    1 回文    -1 非回文int isParlin(int l, int r, string s) {if (dp[l][r])return dp[l][r]; // 返回已有结果if (l >= r) return dp[l][r] = 1; // 一个元素--回文return dp[l][r] = (s[l] == s[r]) ? isParlin(l+1, r-1, s) : -1;}
public:vector<vector<string>> partition(string s) {n = s.size();dp.resize(n, vector<int>(n)); // 初始化为 0dfs(s, 0); // 下标 0 开始分割 [0, ...]return ans; }
};

🌼N 皇后

51. N 皇后 - 力扣(LeetCode)

AC  DFS

col[] 标记列,dfs(int r) 参数 r 标记行

两个点,行差值的绝对值 == 列差值的绝对值,表示(左斜 OR 右斜)冲突

由此,行,列,左斜,右斜是否冲突就可得到

取消标记时,只需要恢复 col[](对列的标记),而不需要恢复 a[](放置位置),因为 a[] 会被后续操作覆盖,但是 col 不会,需要手动恢复现场

坑:int flag = 0; 这个要放在 if(c[j] == 0) 里,如果放在外面,就算列已经被占了,也会进入下一步递归,平白多了许多无关结果 

class Solution {
private:vector<string> temp; // 临时答案vector<vector<string>> ans;int m;int c[10]; // 标记该列(已放置)int a[10]; // 位置, a[3] = 7 表示 (3, 7) 已放置// 第 r 行, 已放置 r 个皇后void dfs(int r) {// 递归出口if (r == m) {ans.push_back(temp);return;}// 遍历每一列for (int j = 0; j < m; ++j) {if (c[j] == 0) { // 这一列未放置int flag = 0; // 标记左斜 / 右斜 冲突for (int i = 0; i < r; ++i) // 遍历前面已放置的每一行if (abs(r - i) == abs(j - a[i])) {flag = 1;break;}// 不冲突,(r, j) 可以放置if (flag == 0) {a[r] = j; // 放置string s = string(m, '.');s[j] = 'Q';temp.push_back(s);c[j] = 1; // 标记(列)dfs(r + 1); c[j] = 0; // 取消标记temp.pop_back(); // 恢复现场}}}}
public:vector<vector<string>> solveNQueens(int n) {m = n; // m 个皇后dfs(0);return ans;}
};

这篇关于hot100 -- 回溯(下)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1023059

相关文章

回溯——9.全排列

力扣题目链接 给定一个 没有重复 数字的序列,返回其所有可能的全排列。 示例: 输入: [1,2,3]输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] 解题思路 问题建模:题目要求给出一个数组的所有排列组合,属于典型的全排列问题,这可以用回溯法来解决。回溯法通过递归的方式,依次将数组中的每个元素放入排列中,直到生成

hot100刷题第1-9题,三个专题哈希,双指针,滑动窗口

求满足条件的子数组,一般是前缀和、滑动窗口,经常结合哈希表; 区间操作元素,一般是前缀和、差分数组 数组有序,更大概率会用到二分搜索 目前已经掌握一些基本套路,重零刷起leetcode hot 100, 套路题按套路来,非套路题适当参考gpt解法。 一、梦开始的地方, 两数之和 class Solution:#注意要返回的是数组下标def twoSum(self, nums: Lis

风控系统之指标回溯,历史数据重跑

个人博客:无奈何杨(wnhyang) 个人语雀:wnhyang 共享语雀:在线知识共享 Github:wnhyang - Overview 回顾 默认你已经看过之前那篇风控系统指标计算/特征提取分析与实现01,Redis、Zset、模版方法。 其中已经介绍了如何利用redis的zset结构完成指标计算,为了方便这篇文章的介绍,还是在正式开始本篇之前回顾一下。 时间窗口 zset

每日一题,力扣leetcode Hot100之238.除自身以外数组的乘积

乍一看这个题很简单,但是不能用除法,并且在O(N)时间复杂度完成或许有点难度。 考虑到不能用除法,如果我们要计算输出结果位置i的值,我们就要获取这个位置左边的乘积和右边的乘积,那么我新设立两个数组L和R。 对于L来说,由于表达的是位置i左边的数的乘积,那么L[0]=1,因为第一个数字左边没数那么为了不影响乘积初始值就设置为1,那么L[1]=L[0]*nums[0],那么L[i]=L[i-1

165-Stamps【回溯】

回溯 给h和k的意思是在k种邮票中选h个邮票 基本的连续邮资问题 15226160 165 Stamps Accepted C++ 0.062 2015-03-27 07:21:37 #include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn = 2

【Hot100】LeetCode—394. 字符串解码

目录 1- 思路栈实现+四种情况处理 2- 实现⭐394. 字符串解码——题解思路 3- ACM 实现 原题链接:394. 字符串解码 1- 思路 栈实现+四种情况处理 ① 遇到数字,进行倍数相加 、②遇到左括号,压栈之前的元素、③遇到右括号弹出,栈进行拼接、④否则遇到字母,直接拼接在 res通过栈,实现先进后出的思想 对于输入 3[a2[c]] 的输入,在读到 3[得

【Hot100】LeetCode—347. 前 K 个高频元素

目录 1- 思路自定义Node结点 + 哈希表实现 2- 实现⭐347. 前 K 个高频元素——题解思路 3- ACM实现 原题连接:347. 前 K 个高频元素 1- 思路 自定义Node结点 + 哈希表实现 ① 自定义 Node 结点: 自定义 Node 结点中有 value 和 cnt 字段,其中 value 为具体的数字,cnt 为具体的值实现 ① getCn

回溯——8.递增子序列

力扣题目链接 给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。 示例: 输入: [4, 6, 7, 7]输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]] 说明: 给定数组的长度不会超过15。数组中的整数范围是 [-100,100]。给定数组中

每日一题,力扣leetcode Hot100之198.打家劫舍

这一道题乍一看可以双层循环暴力解,但是仔细一想有可能最大利益并不是一家隔着一家偷,我可以间隔很多家偷,所以 这个题的思路还是有点像爬楼梯,用动态规划解。 首先确立动态规划的初始条件: 1.dp[0]=nums[0]只有一家 2.dp[1]=max(nums[0],nums[1])有两家选一家多的 然后确立动态规划的循环条件: dp[i]应该是什么 1.第i家能拿,那么dp[i]=n

【代码随想录|回溯part04】

代码随想录|回溯part04 1、46.全排列2、47.全排列 II3.问题 总结 python 1、46.全排列 46.全排列 class Solution:def permute(self, nums):result = []self.backtracking