日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

DFS 算法总结

發布時間:2024/4/15 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 DFS 算法总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

DFS 算法總結

這篇文章會對DFS進行一個總結,列舉的題目則是從LeetCode上面選的;

適用場景:

有三個方面,分別是輸入數據、狀態轉換圖、求解目標;

輸入數據:如果是遞歸數據結構,如單鏈表,二叉樹,集合,則百分之百可以使用深搜;如果是非遞歸數據結構,比如一維數組、二維數組、字符串、圖,則概率要小一些;

狀態轉換圖:樹或者圖;

輸入數據:必須要走到最深(比如對于樹,必須要走到葉子結點)才能得到一個解,這種情況比較適合用深搜;

代碼模版

/*** DFS模版* @param input 輸入數據指針* @param path 當前路徑,也是中間結果* @param result 存放最終結果* @param gap 標記當前位置或距離目標的距離** @return 路徑長度,如果是路徑本身,則不需要返回長度*/template <typename type>void dfs(type & input, type & path, type & result, int cur or gap) {if (數據非法) return 0; // 終止條件if (cur == input.size()) { // 收斂條件 (or gap == 0)將path放入到result中;}if (可以剪枝) return ;for (...) { //執行所有可能的擴展動作1.執行動作,修改path2.dfs(input, path, result, cur + 1 or gap - 1);3.恢復path}}

典型例題

大概遇見這幾種題型:

  • 二叉樹路徑
  • 圖(有向圖遍歷,無向圖遍歷,拓撲排序,最短路徑問題,最小生成樹問題等等)
  • 構造二叉樹
  • 矩陣路徑
  • 構造鏈表
  • 刪除無效括號

二叉樹路徑

例題為求二叉樹路徑
貼上代碼:

/*** 遞歸將更新字符串,到達葉結點時加入到數組中** @param result <#result description#>* @param root <#root description#>* @param t <#t description#>*/void binaryTreePaths(vector<string>& result, TreeNode* root, string t) {if (!root->left && !root->right) {result.push_back(t);return ;}if (root->left) binaryTreePaths(result, root->left, t + "->" + to_string(root->left->val));if (root->right) binaryTreePaths(result, root->right, t + "->" + to_string(root->right->val));}vector<string> binaryTreePaths(TreeNode* root) {vector<string> result;if (!root) return result;binaryTreePaths(result, root, to_string(root->val));return result;}

因為這里面涉及的內容很多,所以就以拓撲排序為例,例題為選課順序;
貼上代碼:

/*** 判斷有向圖是否有環* 通過DFS找環** @param matrix <#matrix description#>* @param visited <#visited description#>* @param idx <#idx description#>* @param flag <#flag description#>** @return <#return value description#>*/bool DFS(vector<unordered_set<int>> &matrix, unordered_set<int> &visited, int idx, vector<bool> &flag) {flag[idx] = true; // 標記該結點訪問過visited.insert(idx);// 找出該結點的所有鄰居結點,如果存在訪問過的結點或者遞歸,則返回truefor (auto it = matrix[idx].begin(); it != matrix[idx].end(); ++it) {if (visited.find(*it) != visited.end() || DFS(matrix, visited, *it, flag)) {return true;}}visited.erase(idx);return false;}bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {vector<unordered_set<int>> matrix(numCourses);// 要想完成第一門課程,則先完成第二門課程(后面的是先要完成的課程)// 構建圖for (int i = 0; i < prerequisites.size(); ++i) {matrix[prerequisites[i].second].insert(prerequisites[i].first);}unordered_set<int> visited; // 記錄一個遞歸訪問過的結點vector<bool> flag(numCourses, false); // 記錄是否訪問過結點/*** 遍歷所有課程,也就是結點* 判斷是否標記過結點,如果沒有則進行DFS判斷是否存在回路,存在回路則返回false*/for (int i = 0; i < numCourses; ++i) {if (!flag[i])// 如果遞歸中存在訪問過的結點,則該拓撲排序是不存在的,也就無法完成課程if (DFS(matrix, visited, i, flag))return false;}return true;}

構造二叉樹

這里分為鏈表構造和數組構造,或是已知前中后序列,構造二叉樹
這里以前序和后序構造二叉樹為例,貼上代碼:

/*** 利用遞歸進行計算左子樹和右子樹** @param inorder <#inorder description#>* @param postorder <#postorder description#>* @param inStart <#inStart description#>* @param inEnd <#inEnd description#>* @param postStart <#postStart description#>* @param postEnd <#postEnd description#>** @return <#return value description#>*/TreeNode* createTree(vector<int>& inorder, vector<int>& postorder, int inStart, int inEnd, int postStart, int postEnd) {if (postorder.empty() || inStart > inEnd || postStart > postEnd)return NULL;// 后序的最后一個結點是跟結點TreeNode * root = new TreeNode(postorder.at(postEnd));int index;for (int i = inStart; i <= inEnd; i++) {if (inorder.at(i) == postorder.at(postEnd)) {index = i;break;}}// 分別定為左子樹和右子樹 (需要注意子樹的邊界問題!!!!!)root->left = createTree(inorder, postorder, inStart, index - 1, postStart, postStart - inStart + index - 1);root->right = createTree(inorder, postorder, index + 1, inEnd, postEnd - inEnd + index, postEnd - 1);return root;}TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {if (postorder.empty())return NULL;return createTree(inorder, postorder, 0, (int)inorder.size() - 1, 0, (int)postorder.size() - 1);}

矩陣路徑

以Longest Increasing Path in a Matrix為例
貼上代碼:

/*** 方法和上面類似,不過利用dirs+循環可以使函數簡化*/vector<vector<int>> dirs = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};int helper(vector<vector<int>>& matrix, vector<vector<int>>& visit, int i, int j, int m, int n) {if (visit[i][j] > 1) return visit[i][j];int result = 1;for (auto dir : dirs) {int x = i + dir[0], y = j + dir[1];if (x < 0 || x >= m || y < 0 || y >= n || matrix[i][j] > matrix[x][y])continue;result = max(result, helper(matrix, visit, i, j, m, n));}visit[i][j] = result;return result;}int longestIncreasingPath2(vector<vector<int>>& matrix) {int m = matrix.size();if (m == 0) return 0;int n = matrix[0].size();int result = 0;vector<vector<int>> visit(m, vector<int>(n, 0));for (int i = 0; i < m; ++i) {for (int j = 0; j < n; ++j) {result = max(result, helper(matrix, visit, i, j, m, n));}}return result;}

構造鏈表

以Populating Next Right Pointers in Each Node為例,貼上代碼:

/*** 遞歸實現** @param root <#root description#>*/void createTree(TreeLinkNode *root) {if (root->left == NULL || root->right == NULL)return ;TreeLinkNode * left = root->left;TreeLinkNode * right = root->right;left->next = right;right->next = root->next ? root->next->left : NULL;createTree(root->left);createTree(root->right);}void connect2(TreeLinkNode *root) {if (root == NULL)return ;root->next = NULL;createTree(root);}

刪除無效括號

以Remove Invalid Parentheses為例,貼上代碼:

/*** DFS+剪枝** @param pair 遇見括號的個數* @param index 記錄字符串s的當前位置* @param remove_left 左括號需要刪除的個數* @param remove_right 右括號需要刪除的個數* @param s 原始字符串* @param solution 生成字符串* @param result 存儲所有的字符串結果*/void helper(int pair, int index, int remove_left, int remove_right, const string& s, string solution, unordered_set<string> &result) {if (index == s.size()) {if (pair == 0 && remove_left == 0 && remove_right == 0)result.insert(solution);return;}if (s[index] == '(') {// 刪除左邊括號if (remove_left > 0) helper(pair, index, remove_left - 1, remove_right, s, solution, result);// 回溯helper(pair + 1, index, remove_left, remove_right, s, solution + s[index], result);}else if (s[index] == ')') {// 刪除右邊括號if (remove_right > 0) helper(pair, index, remove_left, remove_right - 1, s, solution, result);// 回溯if (pair > 0) helper(pair - 1, index, remove_left, remove_right, s, solution + s[index], result);}else {helper(pair, index, remove_left, remove_right, s, solution + s[index], result);}}vector<string> removeInvalidParentheses(string s) {int remove_left = 0, remove_right = 0, pair = 0;unordered_set<string> result; // 處理重復// 計算左右兩邊需要刪除括號的個數for (int i = 0; i < s.size(); ++i) {if (s[i] == '(')remove_left++;else if (s[i] == ')')if (remove_left > 0) remove_left--;else remove_right++;}helper(0, 0, remove_left, remove_right, s, "", result);return vector<string>(result.begin(), result.end());}

總結

回溯法 = 深搜+剪枝
遞歸一定是深搜,深搜不一定是遞歸,因為還可以迭代實現;

遞歸有兩種加速策略,一種是剪枝,對中間結果進行判斷,提前返回;一種是緩存,緩存中間結果,防止重復計算,用空間換時間;

遞歸加緩存,就是memorization,即自頂向下+緩存,memorization不一定用遞歸,就像深搜不一定用遞歸,可以在迭代中使用memorization,遞歸也不一定memorization,可以用memorization加速,但不是必須的;

轉載于:https://www.cnblogs.com/George1994/p/6346751.html

總結

以上是生活随笔為你收集整理的DFS 算法总结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。