算法篇:递归、搜索与回溯算法

一、递归、深搜、穷举vs暴搜vs深搜vs回溯vs剪枝:

01、面试题 08.06. 汉诺塔问题

class Solution 
{
public:void hanota(vector<int>& a, vector<int>& b, vector<int>& c) {dfs(a, b, c, a.size());}void dfs(vector<int>& a, vector<int>& b, vector<int>& c, int n){if(n == 1) {c.push_back(a.back());a.pop_back();return;}dfs(a, c, b, n - 1);c.push_back(a.back());a.pop_back();dfs(b, a, c, n - 1);}
};

02、21. 合并两个有序链表

class Solution 
{
public:ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {if(l1 == nullptr) return l2;if(l2 == nullptr) return l1;if(l1->val <= l2->val){l1->next = mergeTwoLists(l1->next, l2);return l1;}else{l2->next = mergeTwoLists(l1, l2->next);return l2;}}
};

总结:

①循环(迭代)vs递归:分支越多,递归越爽;分支又少又长,则用循环(否则容易栈溢出)。
②递归的展开图,其实就是对一棵树做一次深度优先遍历(dfs)。

03、206. 反转链表

class Solution 
{
public:ListNode* reverseList(ListNode* head) {if(head == nullptr || head -> next == nullptr) return head;ListNode* newhead = reverseList(head->next);head->next->next = head;head->next = nullptr;return newhead;}
};

04、24. 两两交换链表中的节点

class Solution 
{
public:ListNode* swapPairs(ListNode* head) {if(head == nullptr || head->next == nullptr) return head;auto tmp = swapPairs(head->next->next);auto ret = head->next;head->next->next = head;head->next = tmp;return ret;}
};

05、50. Pow(x, n)

解法快速幂  O(N):

class Solution 
{
public:double myPow(double x, int n) {return n < 0 ? 1.0 / pow(x, -(long long)n) : pow(x, n);}double pow(double x, long long n){if(n == 0) return 1.0;auto tmp = pow(x, n / 2);return n % 2 == 0 ? tmp * tmp : tmp * tmp * x;}
};

06、2331. 计算布尔二叉树的值

class Solution 
{
public:bool evaluateTree(TreeNode* root) {if(root->left == nullptr) return root->val == 0 ? false : true;bool left = evaluateTree(root->left);bool right = evaluateTree(root->right);return root->val == 2 ? left | right : left & right;}
};

07、129. 求根节点到叶节点数字之和

class Solution 
{
public:int sumNumbers(TreeNode* root) {return dfs(root, 0);}int dfs(TreeNode* root, int presum){presum = presum * 10 + root->val;if(root->left == nullptr && root->right == nullptr) return presum;int ret = 0;if(root->left) ret += dfs(root->left, presum);if(root->right) ret += dfs(root->right, presum);return ret;}
};

08、814. 二叉树剪枝

class Solution 
{
public:TreeNode* pruneTree(TreeNode* root) {if(root == nullptr) return nullptr;root->left = pruneTree(root->left);root->right = pruneTree(root->right);if(root->left == nullptr && root->right == nullptr && root->val == 0){delete root; // 防止内存泄漏root = nullptr;}return root;}
};

09、98. 验证二叉搜索树

class Solution 
{long prev = LONG_MIN;
public:bool isValidBST(TreeNode* root) {if(root == nullptr) return true;bool left = isValidBST(root->left);// 剪枝if(left == false) return false;bool cur = false;if(root->val > prev) cur = true;// 剪枝if(cur == false) return false;prev = root->val;bool right = isValidBST(root->right); return left && right && cur;}
};

10、230. 二叉搜索树中第K小的元素

class Solution 
{int count = 0;int ret = 0;
public:int kthSmallest(TreeNode* root, int k){count = k;dfs(root);return ret;}void dfs(TreeNode* root){if(root == nullptr || count == 0) return;dfs(root->left);count--;if(count == 0) ret = root->val;dfs(root->right);}
};

11、257. 二叉树的所有路径

class Solution 
{vector<string> ret;
public:vector<string> binaryTreePaths(TreeNode* root){string path; // 不能设置全局变量,否则要手动恢复现场if(root->nullptr) return ret;dfs(root, path);return ret;}void dfs(TreeNode* root, string path) // 不能引用,否则要手动恢复现场{path += to_string(root->val);if(root->left == nullptr && root->right == nullptr){ret.push_back(path);return;}path += "->";if(root->left) dfs(root->left, path); // 剪枝if(root->right) dfs(root->right, path); // 剪枝}
};

12、46. 全排列

// 决策树
class Solution 
{vector<vector<int>> ret;vector<int> path;bool check[7];
public:vector<vector<int>> permute(vector<int>& nums) {dfs(nums);return ret;}void dfs(vector<int>& nums){if(path.size() == nums.size()){ret.push_back(path);return;}for(int i = 0; i < nums.size(); i++){if(check[i] == false){path.push_back(nums[i]);check[i] = true;dfs(nums);// 回溯 -> 恢复现场path.pop_back();check[i] = false;}}}
};

13、78. 子集

解法一:

class Solution 
{vector<vector<int>> ret;vector<int> path;
public:vector<vector<int>> subsets(vector<int>& nums) {dfs(nums, 0);return ret; }void dfs(vector<int>& nums, int pos){if(pos == nums.size()){ret.push_back(path);return;}// 选path.push_back(nums[pos]);dfs(nums, pos + 1);path.pop_back(); // 恢复现场// 不选dfs(nums, pos + 1);}
};

解法二: 

class Solution 
{vector<vector<int>> ret;vector<int> path;
public:vector<vector<int>> subsets(vector<int>& nums) {// 解法二:dfs(nums, 0);return ret; }void dfs(vector<int>& nums, int pos){ret.push_back(path);for(int i = pos; i < nums.size(); i++){path.push_back(nums[i]);dfs(nums, i + 1);path.pop_back(); // 恢复现场}}
};

 14、1863. 找出所有子集的异或总和再求和

class Solution 
{int path = 0;int sum = 0;
public:int subsetXORSum(vector<int>& nums) {dfs(nums, 0);return sum;}void dfs(vector<int>& nums, int pos){sum += path;for(int i = pos; i < nums.size(); i++){path ^= nums[i];dfs(nums, i + 1);path ^= nums[i]; // 亦或运算:消消乐}}
};

 15、47. 全排列 II

算法原理:
前提:先把整个数组排序。
剪枝:
①同一个节点的不同分支中,相同的元素只能选择一次。
②同一个数只能选择一次->check。

两种解法:

①只关心“不合法”的分支。

class Solution 
{vector<vector<int>> ret;vector<int> path;bool check[10];
public:vector<vector<int>> permuteUnique(vector<int>& nums) {sort(nums.begin(), nums.end());dfs(nums, 0);return ret;}void dfs(vector<int>& nums, int pos){if(pos == nums.size()){ret.push_back(path);return;}for(int i = 0; i < nums.size(); i++){// 剪枝if(check[i] == true || (i != 0 && nums[i] == nums[i - 1] && check[i - 1] == false))continue;path.push_back(nums[i]);check[i] = true;dfs(nums, pos + 1);path.pop_back(); // 恢复现场check[i] = false;}}
};

②只关心“合法”的分支。 

class Solution 
{vector<vector<int>> ret;vector<int> path;bool check[10];
public:vector<vector<int>> permuteUnique(vector<int>& nums) {sort(nums.begin(), nums.end());dfs(nums, 0);return ret;}void dfs(vector<int>& nums, int pos){if(pos == nums.size()){ret.push_back(path);return;}for(int i = 0; i < nums.size(); i++){// 剪枝if(check[i] == false && (i == 0 || nums[i - 1] != nums[i] || check[i - 1] != false)){path.push_back(nums[i]);check[i] = true;dfs(nums, pos + 1);path.pop_back(); // 恢复现场check[i] = false;}}}
};

16、17. 电话号码的字母组合

class Solution 
{string hash[10] = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};string path;vector<string> ret;
public:vector<string> letterCombinations(string digits) {if(digits.size() == 0) return ret;dfs(digits, 0);return ret;}void dfs(string& digits, int pos){if(pos == digits.size()){ret.push_back(path);return;}for(auto ch : hash[digits[pos] - '0']){path.push_back(ch);dfs(digits,pos + 1);path.pop_back();}}
};

17、22. 括号生成

有效的括号组合:
①左括号的数量=有括号的数量。
②从头开始的任意一个字串,左括号的数量>=右括号的数量。

class Solution 
{int left, right, n;vector<string> ret;string path;
public:vector<string> generateParenthesis(int _n) {n = _n;dfs();return ret; }void dfs(){// 递归出口if(right == n){ret.push_back(path);return;}if(left < n) // 添加左括号{path.push_back('(');left++;dfs();path.pop_back(); // 恢复现场left--;}if(right < left) // 添加右括号{path.push_back(')');right++;dfs();path.pop_back(); // 恢复现场right--;}}
};

​18、77. 组合

class Solution 
{int n, k;vector<vector<int>> ret;vector<int> path;
public:vector<vector<int>> combine(int _n, int _k) {n = _n;k = _k;dfs(1);return ret;}void dfs(int start){if(path.size() == k){ret.push_back(path);return;}for(int i = start; i <= n; i++) // 剪枝{path.push_back(i);dfs(i + 1);path.pop_back(); // 恢复现场}}
};

19、494. 目标和

path作为全局变量:

class Solution 
{int path, ret, aim;
public:int findTargetSumWays(vector<int>& nums, int target) {aim = target;dfs(nums, 0);return ret;}void dfs(vector<int>& nums, int pos){if(pos == nums.size()){if(path == aim) ret++;   return;}// 加法path += nums[pos];dfs(nums, pos + 1);path -= nums[pos]; // 恢复现场// 减法path -= nums[pos];dfs(nums, pos + 1);path += nums[pos]; // 恢复现场}
};

path作为参数:

class Solution 
{int ret, aim;
public:int findTargetSumWays(vector<int>& nums, int target) {aim = target;dfs(nums, 0, 0);return ret;}void dfs(vector<int>& nums, int pos,int path){if(pos == nums.size()){if(path == aim) ret++;   return;}// 加法dfs(nums, pos + 1, path + nums[pos]);// 减法dfs(nums, pos + 1, path - nums[pos]);}
};

20、39. 组合总和

解法一:

class Solution 
{int sum, aim;vector<int> path;vector<vector<int>> ret;
public:vector<vector<int>> combinationSum(vector<int>& nums, int target) {aim = target;dfs(nums, 0, 0);return ret;}void dfs(vector<int>& nums, int pos, int sum){if(sum == aim){ret.push_back(path);return;}if(pos == nums.size() || sum > aim) return;for(int i = pos; i < nums.size(); i++){path.push_back(nums[i]);dfs(nums, i, sum + nums[i]);path.pop_back();}}
};

解法二:

class Solution 
{int sum, aim;vector<int> path;vector<vector<int>> ret;
public:vector<vector<int>> combinationSum(vector<int>& nums, int target) {aim = target;dfs(nums, 0, 0);return ret;}void dfs(vector<int>& nums, int pos, int sum){if(sum == aim){ret.push_back(path);return;}if(pos == nums.size() || sum > aim) return;// 枚举个数for(int k = 0; k * nums[pos] + sum <= aim; k++){if(k) path.push_back(nums[pos]);dfs(nums, pos + 1, sum + k * nums[pos]);}// 恢复现场for(int k = 1; k * nums[pos] + sum <= aim; k++){path.pop_back();}}
};

21、784. 字母大小写全排列

class Solution 
{vector<string> ret;string path;
public:vector<string> letterCasePermutation(string s) {dfs(s, 0);return ret;}void dfs(string& s, int pos){if(pos == s.size()){ret.push_back(path);return;}char ch = s[pos];path.push_back(ch);dfs(s, pos + 1);path.pop_back();if(ch < '0' || ch > '9'){char tmp = change(ch);path.push_back(tmp);dfs(s, pos + 1);path.pop_back();}}char change(char ch){if(ch >= 'a' && ch <= 'z') ch -= 32;else ch += 32;return ch;}
};

22、526. 优美的排列

class Solution 
{bool check[16];int ret;
public:int countArrangement(int n) {dfs(1, n);return ret;}void dfs(int pos, int n){if(pos == n + 1){ret++;return;}for(int i = 1; i <= n; i++){if(!check[i] && (pos % i == 0 || i % pos == 0)){check[i] = true;dfs(pos + 1, n);check[i] = false; // 恢复现场}}}
};

23、51. N 皇后

class Solution 
{bool checkCol[10], checkDig1[20], checkDig2[20];vector<vector<string>> ret;vector<string> path;int n; 
public:vector<vector<string>> solveNQueens(int _n) {n = _n;path.resize(n);for(int i = 0; i < n; i++)path[i].append(n, '.');dfs(0);return ret;}void dfs(int row){if(row == n){ret.push_back(path);return;}for(int col = 0; col < n; col++) // 尝试在这一行放皇后{// 剪枝if(!checkCol[col] && !checkDig1[row - col + n] && !checkDig2[row + col]){path[row][col] = 'Q';checkCol[col] = checkDig1[row - col + n] = checkDig2[row + col] = true;dfs(row + 1);// 恢复现场path[row][col] = '.';checkCol[col] = checkDig1[row - col + n] = checkDig2[row + col] = false;}}}
};

24、36. 有效的数独

class Solution 
{bool row[9][10];bool col[9][10];bool grid[3][3][10];public:bool isValidSudoku(vector<vector<char>>& board) {for(int i = 0; i < 9; i++)for(int j = 0; j < 9; j++){if(board[i][j] != '.'){int num = board[i][j] - '0';// 是否是有效的if(row[i][num] || col[j][num] || grid[i / 3][j / 3][num])return false;row[i][num] = col[j][num] = grid[i / 3][j / 3][num] = true;}}return true;}
};

25、37. 解数独

class Solution 
{bool row[9][10], col[9][10], grid[3][3][10];
public:void solveSudoku(vector<vector<char>>& board) {// 初始化for(int i = 0; i < 9; i++){for(int j = 0; j < 9; j++){if(board[i][j] != '.'){int num = board[i][j] - '0';row[i][num] = col[j][num] = grid[i / 3][j / 3][num] = true;}}}dfs(board);}bool dfs(vector<vector<char>>& board){for(int i = 0; i < 9; i++){for(int j = 0; j < 9; j++){if(board[i][j] == '.'){// 填数for(int num = 1; num <= 9; num++){if(!row[i][num] && !col[j][num] && !grid[i / 3][j / 3][num]){board[i][j] = '0' + num;row[i][num] = col[j][num] = grid[i / 3][j / 3][num] = true;if(dfs(board) == true) return true;// 恢复现场board[i][j] = '.';row[i][num] = col[j][num] = grid[i / 3][j / 3][num] = false;}}return false;}}}return true;}
};

26、79. 单词搜索

class Solution 
{bool vis[7][7];int m, n;
public:bool exist(vector<vector<char>>& board, string word) {m = board.size(), n = board[0].size();for(int i = 0; i < m; i++)for(int j = 0; j < n; j++){if(board[i][j] == word[0]){vis[i][j] = true;if(dfs(board, i, j, word, 1)) return true;vis[i][j] = false;}}return false;}int dx[4] = {0, 0, -1, 1};int dy[4] = {1, -1, 0, 0};bool dfs(vector<vector<char>>& board, int i, int j, string& word, int pos){if(pos == word.size()) return true;// 向量的位置,定义上下左右四个位置for(int k = 0; k < 4; k++){int x = i + dx[k], y = j + dy[k];if(x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && board[x][y] == word[pos]){vis[x][y] = true;if(dfs(board, x, y, word, pos + 1)) return true;vis[x][y] = false;}}return false;}
};

27、1219. 黄金矿工

class Solution 
{bool vis[16][16];int dx[4] = {0, 0, -1, 1};int dy[4] = {1, -1, 0, 0};int m, n, ret;
public:int getMaximumGold(vector<vector<int>>& g) {m =  g.size(), n =  g[0].size();for(int i = 0; i < m; i++)for(int j = 0; j < n; j++){if(g[i][j]){vis[i][j] = true;dfs(g, i, j, g[i][j]);vis[i][j] = false;}}return ret;}void dfs(vector<vector<int>>& g, int i, int j, int path){ret = max(ret, path);for(int k = 0; k < 4; k++){int x = i + dx[k], y = j + dy[k];if(x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && g[x][y]){vis[x][y] = true;dfs(g, x, y, path + g[x][y]);vis[x][y] = false;}}}
};

 28、980. 不同路径 III

class Solution 
{bool vis[21][21];int dx[4] = {0, 0, -1, 1};int dy[4] = {1, -1, 0, 0};int ret;int m, n, step;
public:int uniquePathsIII(vector<vector<int>>& grid) {m = grid.size(), n = grid[0].size();int bx = 0, by = 0;for(int i = 0; i < m; i++)for(int j = 0; j < n; j++)if(grid[i][j] == 0) step++;else if(grid[i][j] == 1){bx = i;by = j;}step += 2;vis[bx][by] = true;dfs(grid, bx, by, 1);return ret;}void dfs(vector<vector<int>>& grid, int i, int j, int count){if(grid[i][j] == 2){if(count == step) // 判断是否合法ret++;return;}for(int k = 0; k < 4; k++){int x = i + dx[k], y = j + dy[k];if(x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] != -1){vis[x][y] = true;dfs(grid, x, y, count + 1);vis[x][y] = false;}}    }
};

二、floodfill算法: 

30、 733. 图像渲染

class Solution 
{int m, n, prev;int dx[4] = {0, 0, -1, 1};int dy[4] = {1, -1, 0, 0};
public:vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {if(image[sr][sc] == color) return image;m = image.size(), n = image[0].size();prev = image[sr][sc];dfs(image, sr, sc, color);return image;}void dfs(vector<vector<int>>& image, int i, int j, int color){image[i][j] = color;for(int k = 0; k < 4; k++){int x = i + dx[k], y = j + dy[k];if(x >= 0 && x < m && y >= 0 && y < n && image[x][y] == prev){dfs(image, x, y, color);}}}
};

 31、200. 岛屿数量

class Solution 
{vector<vector<bool>> vis;int dx[4] = {0, 0, -1, 1};int dy[4] = {1, -1, 0, 0};int m, n, ret;
public:int numIslands(vector<vector<char>>& grid) {m = grid.size(), n = grid[0].size();vis = vector<vector<bool>>(m, vector<bool>(n));for(int i = 0; i < grid.size(); i++)for(int j = 0; j < grid[0].size(); j++){if(!vis[i][j] && grid[i][j] == '1'){ret++;dfs(grid, i, j);}}return ret;}void dfs(vector<vector<char>>& grid, int i, int j){vis[i][j] = true;for(int k = 0; k < 4; k++){int x = i + dx[k], y = j + dy[k];if(x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] == '1'){dfs(grid, x, y);}}}
};

32、 695. 岛屿的最大面积​​​​​​​

class Solution 
{vector<vector<bool>> vis;int dx[4] = {0, 0, -1, 1};int dy[4] = {1, -1, 0, 0};int m, n, count;
public:int maxAreaOfIsland(vector<vector<int>>& grid) {m = grid.size(), n = grid[0].size();vis = vector<vector<bool>>(m, vector<bool>(n));int ret = 0;for(int i = 0; i < grid.size(); i++)for(int j = 0; j < grid[0].size(); j++){if(!vis[i][j] && grid[i][j] == 1){count = 0;dfs(grid, i, j);ret = max(ret, count);}}return ret;}void dfs(vector<vector<int>>& grid, int i, int j){count++;vis[i][j] = true;for(int k = 0; k < 4; k++){int x = i + dx[k], y = j + dy[k];if(x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] == 1){dfs(grid, x, y);}}}
};

33、 130. 被围绕的区域

class Solution 
{   // 正难则反int dx[4] = {0, 0, -1, 1};int dy[4] = {1, -1, 0, 0};int m, n;
public:void solve(vector<vector<char>>& board) {// 1、把边界的0相连的连通块,全部修改成'.'m = board.size(), n = board[0].size();for(int j = 0; j < n; j++){if(board[0][j] == 'O') dfs(board, 0, j);if(board[m - 1][j] == 'O') dfs(board, m - 1, j);}for(int i = 0; i < m; i++){if(board[i][0] == 'O') dfs(board, i, 0);if(board[i][n - 1] == 'O') dfs(board, i, n - 1);}// 2、还原for(int i = 0; i < m; i++)for(int j = 0; j < n; j++){if(board[i][j] == '.') board[i][j] = 'O';else if(board[i][j] == 'O') board[i][j] = 'X';}}void dfs(vector<vector<char>>& board, int i, int j){board[i][j] = '.';for(int k = 0; k < 4; k++){int x = i + dx[k], y = j + dy[k];if(x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'O'){dfs(board, x, y);}}}
};

 34、417. 太平洋大西洋水流问题

class Solution 
{int dx[4] = {0, 0, -1, 1};int dy[4] = {1, -1, 0, 0};int m, n;
public:vector<vector<int>> pacificAtlantic(vector<vector<int>>& h) {m = h.size(), n = h[0].size();vector<vector<bool>> pac(m, vector<bool>(n));vector<vector<bool>> atl(m, vector<bool>(n));// 1、先处理 pac 洋for(int j = 0; j < n; j++) dfs(h, 0, j, pac);for(int i = 0; i < m; i++) dfs(h, i, 0, pac);// 2、先处理 atl 洋for(int i = 0; i < m; i++) dfs(h, i, n - 1, atl);for(int j = 0; j < n; j++) dfs(h, m - 1, j, atl);vector<vector<int>> ret;for(int i = 0; i < m; i++)for(int j = 0; j < n; j++)if(pac[i][j] && atl[i][j])ret.push_back({i, j});return ret;}void dfs(vector<vector<int>>& h, int i, int j, vector<vector<bool>>& vis){vis[i][j] = true;for(int k = 0; k < 4; k++){int x = i + dx[k], y = j + dy[k];if(x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && h[x][y] >= h[i][j]){dfs(h, x, y, vis);}}}
};

35、529. 扫雷游戏 

class Solution 
{int dx[8] = {0, 0, 1, -1, 1, 1, -1, -1};int dy[8] = {1, -1, 0, 0, 1, -1, 1, -1};int m, n;public:vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click) {m = board.size(), n = board[0].size();int x = click[0], y = click[1];if(board[x][y] == 'M') // 直接点到地雷{board[x][y] = 'X';return board;        }dfs(board, x, y);return board;}void dfs(vector<vector<char>>& board, int i, int j){// 统计一下周围的地雷个数int count = 0;for(int k = 0; k < 8; k++){int x = i + dx[k], y = j + dy[k];if(x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'M'){count++;}}if(count) // 周围有地雷{board[i][j] = count + '0';return;}else // 周围没有地雷{board[i][j] = 'B';for(int k = 0; k < 8; k++){int x = i + dx[k], y = j + dy[k];if(x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'E'){dfs(board, x, y);}}}}
};

三、记忆化搜索:

37、509. 斐波那契数

解法一:递归  O(2^N)

解法二:记忆化搜索  O(N)
①是什么:带备忘录的递归。
②怎么实现:添加一个备忘录<可变参数,返回值>,把结果放到备忘录里面,在每次进入递归的时候向备忘录里查找一下。

class Solution 
{int memo[31]; // memory 
public:int fib(int n) {// 初始化memset(memo, -1, sizeof memo);return dfs(n);}int dfs(int n){// 往备忘录里查找一下if(memo[n] != -1) // 剪枝{return memo[n];}if(n == 0 || n == 1){memo[n] = n; // 返回之前放进备忘录里面return n;}memo[n]  = dfs(n - 1) + dfs(n - 2); // 返回之前放进备忘录里面return memo[n];}
};

解法三:
①确定状态表示(dfs函数的含义);
②推导状态转移方程(dfs的函数体);
③初始化(dfs函数的递归出口);
④确定填表顺序(填写备忘录的顺序);
⑤确定返回值(主函数是如何调用dfs的)。

class Solution 
{int dp[31];
public:int fib(int n) {// 动态规划dp[0] = 0, dp[1] = 1;for(int i = 2; i <= n; i++)dp[i] = dp[i - 1] + dp [i - 2];return dp[n];}
};

总结:动态规划和记忆化搜索的本质:
①暴力解法(暴搜)。
②对暴力解法的优化:把已经计算过的值存起来。

问题:
①所有的递归(暴搜、深搜),都能改成记忆化搜索吗?
不是的,只有在递归的过程中,出现了大量的完全相同的问题时,才能用记忆化搜索的方式优化。
②带备忘录的递归 vs 带备忘录的动态规划 vs 记忆化搜索?
都是一回事。
③自顶向下 vs 自底向上?
记忆化搜索:自顶向下。
常规动态规划:自底向上。
④暴搜->记忆化搜索->动态规划 vs 常规动态规划?
80%可以用前者的思考方式,但有些题用暴搜的时间成本太高了(暴搜只是为我们确定状态表示提供方向)。

38、​​​​​​​62. 不同路径

解法一:暴搜->记忆化搜索:

class Solution 
{
public:int uniquePaths(int m, int n) {// 记忆化搜索vector<vector<int>> memo(m + 1, vector<int>(n + 1));return dfs(m, n, memo);}int dfs(int i, int j, vector<vector<int>>& memo){if(memo[i][j] != 0) return memo[i][j];if(i == 0 || j == 0) return 0;if(i == 1 && j == 1){memo[i][j] = 1;return 1;}memo[i][j] = dfs(i - 1, j, memo) + dfs(i, j - 1, memo);return memo[i][j];}
};

解法二:动态规划:

class Solution 
{
public:int uniquePaths(int m, int n) {// 1、创建dp表vector<vector<int>> dp(m + 1, vector<int>(n + 1));// 2、初始化dp[0][1] = 1;// 3、填表for(int i = 1; i <= m; i++){for(int j = 1; j <= n; j++){dp[i][j] = dp[i-1][j] + dp[i][j-1];}}// 4、返回值return dp[m][n];}
};

39、​​​​​​​​​​​​​​300. 最长递增子序列

解法一:暴搜->记忆化搜索:

class Solution 
{
public:int lengthOfLIS(vector<int>& nums) {int n = nums.size();vector<int> memo(n);int ret = 0;for(int i = 0; i < nums.size(); i++)ret = max(ret, dfs(i, nums, memo));return ret;}int dfs(int pos, vector<int>& nums, vector<int>& memo){if(memo[pos] != 0) return memo[pos];int ret = 1;for(int i = pos + 1; i < nums.size(); i++){if(nums[i] > nums[pos]){ret = max(ret, dfs(i, nums, memo) + 1);}}memo[pos] = ret;return memo[pos];}
};

解法二:动态规划:

class Solution 
{int ret = 0;int n = nums.size();
public:int lengthOfLIS(vector<int>& nums) {// 动态规划vector<int> dp(n, 1);// 填表顺序:从后往前(以i为首的最长递增子序列)for(int i = n - 1; i >= 0; i--){for(int j = i + 1; j < n; j++){if(nums[j] > nums[i]){dp[i] = max(dp[i], dp[j] + 1);}}ret = max(ret, dp[i]);}return ret;}
};

 40、375. 猜数字大小 II?

class Solution 
{int memo[201][201];
public:int getMoneyAmount(int n) {return dfs(1, n);}int dfs(int left, int right){if(left >= right) return 0;if(memo[left][right] != 0) return memo[left][right];int ret = INT_MAX;for(int head = left; head <= right; head++) // 选择头结点·{int x = dfs(left, head - 1);int y = dfs(head + 1, right);ret = min(ret, head + max(x, y));}memo[left][right] = ret;return memo[left][right];}
};

  41、329. 矩阵中的最长递增路径

class Solution 
{int dx[4] = {0, 0, -1, 1};int dy[4] = {1, -1, 0, 0};int m, n;int memo[201][201];public:int longestIncreasingPath(vector<vector<int>>& matrix) {int ret = 0;m = matrix.size(), n = matrix[0].size();for(int i = 0; i < m; i++)for(int j = 0; j < n; j++){ret = max(ret, dfs(matrix, i, j));}return ret;}int dfs(vector<vector<int>>& matrix, int i, int j){if(memo[i][j] != 0) return memo[i][j];int ret = 1;for(int k = 0; k < 4; k++){int x = i + dx[k], y = j + dy[k];if(x >= 0 && x < m && y >= 0 && y < n && matrix[x][y] > matrix[i][j]){ret = max(ret, dfs(matrix, x, y) + 1);}}memo[i][j] = ret;return memo[i][j];}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/664697.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于WordPress开发微信小程序2:决定开发一个wordpress主题

上一篇&#xff1a;基于WordPress开发微信小程序1&#xff1a;搭建Wordpress-CSDN博客 很快发现一个问题&#xff0c;如果使用别人的主题模板&#xff0c;多多少少存在麻烦&#xff0c;所以一咬牙&#xff0c;决定自己开发一个主题模板&#xff0c;并且开源在gitee上&#xff…

C++类和对象的属性

C类和对象的属性 千钧一发&#xff0c;让一根头发去承受三万斤的重量&#xff0c;但是它没有断。-----余华 const修饰结构体指针 内部值不能修改&#xff0c;即&#xff1a;只能读&#xff0c;不能写。防止误操作 #include <iostream> using namespace std;struct Po…

计算机网络自顶向下Wireshark labs-HTTP

我直接翻译并在题目下面直接下我的答案了。 1.基本HTTP GET/response交互 我们开始探索HTTP&#xff0c;方法是下载一个非常简单的HTML文件 非常短&#xff0c;并且不包含嵌入的对象。执行以下操作&#xff1a; 启动您的浏览器。启动Wireshark数据包嗅探器&#xff0c;如Wir…

【数据结构】 - 队列 栈

theme: smartblue 一、队列 1、概念 队列&#xff08;Queue&#xff09;是一种常见的数据结构&#xff0c;它按照先进先出&#xff08;First In First Out&#xff0c;FIFO&#xff09;的原则进行元素操作。在队列中&#xff0c;新元素总是被添加到队列的末尾&#xff0c;而…

spring-boot-actuator 服务监控

1 概述 服务启动时&#xff0c;通过spring-boot-actuator 监控es等服务是否连接成功等 2 依赖 <!-- 服务监控 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId><…

2024年美国大学生数学建模竞赛(E题)财产保险建模|MCDA/随机森林建模解析,小鹿学长带队指引全代码文章与思路

我是鹿鹿学长&#xff0c;就读于上海交通大学&#xff0c;截至目前已经帮500人完成了建模与思路的构建的处理了&#xff5e; 本文运用利用时间序列和强化学习结合DQN算法&#xff0c;解决保险业可持续性问题&#xff1b;采用MCDA和随机森林&#xff0c;应对地产业保险挑战&…

[Unity Sentis] Unity Sentis 详细步骤工作流程

文章目录 1. 导入模型文件支持的模型创建运行时模型导入错误 2. 为模型创建输入将数组转换为张量创建多个输入进行操作 3. 创建一个引擎来运行模型创建一个Worker后端类型 4. 运行模型5. 获取模型的输出获取张量输出多个输出打印输出 1. 导入模型文件 要导入 ONNX 模型文件&am…

电脑怎么录屏?打造专业级视频内容!

随着科技的进步&#xff0c;电脑已经深入到我们的日常生活和工作中。而在这个数字时代&#xff0c;录制屏幕内容变得日益重要。无论是制作教程、分享游戏技巧&#xff0c;还是记录重要的演示&#xff0c;录屏都是一个不可或缺的功能。可是电脑怎么录屏呢&#xff1f;本文将深入…

Cmake语法学习3:语法

1.双引号 1.1 命令参数 1&#xff09;介绍 命令中多个参数之间使用空格进行分隔&#xff0c;而 cmake 会将双引号引起来的内容作为一个整体&#xff0c;当它当成一个参数&#xff0c;假如你的参数中有空格&#xff08;空格是参数的一部分&#xff09;&#xff0c;那么就可以使…

web前端较新的前端技术和趋势

Web前端的技术发展迅速&#xff0c;不断有新工具和框架出现。以下是一些较新的前端技术和趋势&#xff1a; 框架和库&#xff1a; React&#xff1a;Facebook开发的一个用于构建用户界面的JavaScript库&#xff0c;目前非常流行。 Vue.js&#xff1a;一个渐进式JavaScript框架…

java DateTimeFormatter 使用

DateTimeFormatterBuilder dateTimeFormatterBuilder new DateTimeFormatterBuilder().append(DateTimeFormatter .ofPattern(“[MM/dd/yyyy]” “[dd-MM-yyyy]” “[yyyy-MM-dd]” “[yyyy-M-d]” “[dd MMM yyyy]”)); LocalDate dd LocalDate.parse(“03 Feb 2024”,…

国自然结题报告为什么不能下载?如何解决

一直以来&#xff0c;国自然的结题报告都不是只能在线查看&#xff0c;这给很多科研人员带来一些不方便&#xff0c;因为在线查看的话&#xff0c;受限于网络&#xff0c;必须有网的时候才能看&#xff0c;并且官方网站的响应比较慢&#xff0c;想快速翻页&#xff0c;基本不可…

《苍穹外卖》知识梳理P2-公共类说明

《苍穹外卖》知识梳理P2 上一节中&#xff0c;进行了项目结构的搭建知识梳理P1 公共类说明 所有公共类都位于common模块下&#xff0c;由于每个包下的类很多&#xff0c;在此不一一列举&#xff1b; 1.constant包&#xff1a;主要存放常量类&#xff0c;类中只有静态常量&am…

leetcode-链表专题

25.K个一组翻转链表 题目链接 25. K 个一组翻转链表 - 力扣&#xff08;LeetCode&#xff09; 解题思路 # Definition for singly-linked list. # class ListNode: # def __init__(self, val0, nextNone): # self.val val # self.next next class So…

【Java程序设计】【C00240】基于Springboot的班级综合测评管理系统(有论文)

基于Springboot的班级综合测评管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的班级综合测评管理系统 本系统分为学生功能模块、管理员功能模块以及教师功能模块。 管理员功能模块&#xff1a;管理员功能…

MySQL中DML操作(九)

MySQL中DML操作&#xff08;九&#xff09; DML语言是操作数据的语言 一、添加数据(INSERT) 1.1 选择插入 INSERT INTO 表名(列名 1 &#xff0c;列名 2 &#xff0c;列名 3.....) VALUES(值 1 &#xff0c;值 2 &#xff0c;值 3......); 1.2 完成插入 INSERT INTO 表名…

L1-020 帅到没朋友分数 20

当芸芸众生忙着在朋友圈中发照片的时候&#xff0c;总有一些人因为太帅而没有朋友。本题就要求你找出那些帅到没有朋友的人。 输入格式&#xff1a; 输入第一行给出一个正整数N&#xff08;≤100&#xff09;&#xff0c;是已知朋友圈的个数&#xff1b;随后N行&#xff0c;每…

MySQL学习记录——일 MySQL 安装、配置

文章目录 1、卸载内置环境2、安装MySQL3、启动4、登录5、配置my.cnf 当前环境是1核2G云服务器&#xff0c;CentOS7.6。要在root用户下进行操作 1、卸载内置环境 云服务器中有可能会自带mysql还有mariadb这样的数据库服务&#xff0c;在安装我们mysql前&#xff0c;得先查找一下…

迪文串口屏数据的隐藏功能

一、概述 由于项目中在使用迪文屏显示数据的时候&#xff0c;需要在数据为0的时候不显示0&#xff0c;而迪文屏默认的数据变量在无值的时候显示为0&#xff0c;此时可以使用数据的隐藏功能指令 二、具体实现方法 1、可以使用描述指针地址来实现数据的隐藏&#xff0c;查看应用…

大模型重塑车载语音交互:赛道巨头如何引领新周期?

车载语音交互赛道正进入新一轮竞争周期。 高工智能汽车注意到&#xff0c;传统车载语音交互赛道当前基本已进入成熟期&#xff0c;主要为任务型助手&#xff0c;包括从单轮对话到多轮对话&#xff0c;单音区到多音区&#xff0c;从单一的导航、多媒体娱乐等座舱功能扩展智能驾…