1 深度优先遍历

最常见的优化:

  • 1 记忆化搜索: 使用hash记录遍历起点对应的值,然后直接从hash中获得,避免重复计算

  • 2 常见算法:
    对于欧拉图和半欧拉图算欧拉路径:hierholzer算法

2 例子

0332HierholzerToFindEulerPath 找欧拉路径

1 题目

https://leetcode-cn.com/problems/reconstruct-itinerary/

2 解题思路

hierholzer算法参考:https://www.geeksforgeeks.org/hierholzers-algorithm-directed-graph/
https://blog.csdn.net/qq_34292517/article/details/105463522?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_title~default-0.pc_relevant_default&spm=1001.2101.3001.4242.1&utm_relevant_index=3

  • 1 自己的思路:

    • 1.1 主要是使用hierholzer算法找到欧拉路径,由于需要字典序,那么我们邻接表则使用优先队列来存储
  • 2 hierholzer算法
    • 2.1 dfs,当一个节点没邻居了(因为每访问一条边就删除一条边)
    • 2.2 将节点入栈reversePath
    • 2.3 dfs完成,reversePath则为逆序栈
  • 3 欧拉图等等理解:参考上述第二篇文章

基本概念

圈:任选图中一个顶点为起点,沿着不重复的边,经过不重复的顶点为途径,之后又回到起点的闭合途径称为圈。

欧拉路径:通过图中所有边一次且仅一次遍历所有顶点的路径称为欧拉(Euler)路径;

欧拉回路:通过图中所有边一次且仅一次行遍所有顶点的回路称为欧拉回路;

欧拉图:具有欧拉回路的图称为欧拉图;

半欧拉图:有欧拉路径但没有欧拉回路的图称为半欧拉图。

欧拉图与半欧拉图的判定:

G是欧拉图 ⇔ G中所有顶点的度均为偶数 ⇔ G是若干个边不重的圈的并。

G是半欧拉图 ⇔ G中恰有两个奇数度顶点。

class Solution {public:int edgeNums = -1;int nodeNums = -1;unordered_map<string, int> nodes;unordered_map<int, string> toStr;vector<string> findItinerary(vector<vector<string>>& tickets) {// map str to int according to dictionary orderset<string, std::less<string>> airports;for(auto& vec : tickets) {airports.insert(vec[0]);airports.insert(vec[1]);}int i = 0;for(auto& str : airports) {toStr[i] = str;nodes[str] = i++;}// construct the adj tableint nodeNums = airports.size();int edgeNums = tickets.size();// vector<vector<int>> table(nodeNums, vector<int>(0));vector<priority_queue<int, vector<int>, greater<int>>> table(nodeNums, priority_queue<int, vector<int>, greater<int>>());for(auto& vec : tickets) {table[nodes[vec[0]]].push(nodes[vec[1]]);}vector<string> strRes;vector<priority_queue<int, vector<int>, greater<int>>> tableTmp(table);vector<int> res;dfs(nodes["JFK"], tableTmp, res);reverse(res.begin(), res.end());for(auto& node : res) {strRes.push_back(toStr[node]);}return strRes;}// void dfs(int st, vector<priority_queue<int, vector<int>, greater<int>>>& map, vector<int>& res) {while(!map[st].empty()) {int nextSt = map[st].top();map[st].pop();// cout << "from to: " << toStr[st] << " -> " << toStr[nextSt] << endl;dfs(nextSt, map, res);}res.emplace_back(st);}
};

0753crackSafe 破解保险箱(变形欧拉路)

1 题目

https://leetcode-cn.com/problems/freedom-trail/
https://leetcode-cn.com/problems/reconstruct-itinerary/

2 解题思路

  • 1 对于 n = 3, k = 3, 我们的图的节点为(k ^ (n-1)个): 00, 01, …, 22, 然后每个节点都有k个边,这样一共是k^n个边

    • 1.1 那么如何认为走过一条边就是尝试一次密码呢?
    • 1.2 比如: 00的邻接顶点为0,1,2, 那么当dfs访问从00节点到其邻接点分别组成的边为000,001,002,则他们对应的下一跳就为: 00,01,02,也就是取当前dfs访问得到的边的后n-1位
  • 2 hierholzer算法
    • 2.0 选择一个节点开始dfs
    • 2.1 当一个节点没邻居了
    • 2.2 将节点入栈reversePath
    • 2.3 dfs完成,reversePath则为逆序栈
class Solution {public:int n = -1;int k = -1;vector<char> kVec;string crackSafe(int n, int k) {// since for each bit, there a k's possiblity// so the final str's length = k^n// consider a G, vertices are {0, 1, ..., k-1}// for each edge: vi -> vj(vi could equal vj), // there shall be n-1's such same edge// we just need a way to walk through the G// try hierholzer algoif(1 == n) {string tmpRes = "";for(int i = 0; i < k; ++i) {tmpRes.push_back(i + '0');}return tmpRes;}this->k = k;this->n = n;for(int i = 0; i < k; ++i) {kVec.push_back(i + '0');}unordered_set<string> seen;unordered_map<string, vector<char>> graph;buildGraph("", n - 1, graph);string stStr(n-1, '0');string res = "";hierholzer(stStr, graph, res, seen);// when n=3, k=3, we start from "00" node, so we add reverse of "00" to the end of the res, cause hierholzer produce a reverse eular path (start from "00", end to "00")res += stStr;return res;}void buildGraph(string tmp, int leftBitNum, unordered_map<string, vector<char>>& graph) {if(0 == leftBitNum) {// cout << "len: " << leftBitNum << "finish node: " << tmp << endl;graph[tmp] = kVec;return;}for(int st = 0; st < k; ++st) {buildGraph(tmp + kVec[st], leftBitNum-1, graph);}}void hierholzer(string st, unordered_map<string, vector<char>>& graph, string& res, unordered_set<string>& seen) {// cout << "doing : " << st << endl;bool hasOut = false;for(int edIdx = 0; edIdx < k; ++edIdx) {string curEdge(st);curEdge.push_back(graph[st][edIdx]);if(seen.count(curEdge)) {continue;}hasOut = true;string nextSt = curEdge.substr(1);// cout << "st -> nextSt: " << st << " -> " << nextSt << endl;seen.insert(curEdge);// cout << "see edge: " << curEdge << endl; hierholzer(nextSt, graph, res, seen); // post orderres.push_back(graph[st][edIdx]); // hierholzer}}};

0514wayOfFreedom 自由之路

1 题目

https://leetcode-cn.com/problems/freedom-trail/

2 解题思路

  • 1 首先考虑到,key中的每一个字符,环的所有同种字符的所有位置都是遍历的可能

    • 1.1 于是使用dfs去尝试对于key的一个字符的每个位置即可,然后讲每个位置得到的结果比较取一个最小值
    • 1.2 1.1中提到的算法肯定是有问题的,比如key: abc, 然后ring: aaabbbccc
      • 会出现哪种情况呢? ring中a的三个位置都会搜索,然后对于剩下的key和ring bc以及bbbccc会由于a有三个位置而搜索了三遍,所以需要记忆化搜索
    • 1.3 使用memo[i][j]记录: key[i:]和ring[j:]对应的最小步数即可
  • 2 经过1的思考,也较为容易知道,本题目,动态规划也能做
  • 3 写代码的教训,由于我将dfsmemo写好,然后调用dfs的地方也改了,但是依然超时?
    • 3.1 因为dfsmemo递归调用不是自身,而是dfs函数,所以务必及时清理不需要的代码
class Solution {public:int ringLen = -1;int keyLen = -1;int findRotateSteps(string ring, string key) {unordered_map<char, vector<int>> ringMap;ringLen = ring.length();keyLen = key.length();int pos = 0;for(auto& c : ring) {if(0 == ringMap.count(c)) {vector<int> tmp = {pos};ringMap[c] = tmp;         } else {ringMap[c].push_back(pos);}++pos;}vector<vector<int>> memo(keyLen, vector<int>(ringLen, INT_MAX));return dfsMemo(0, 0, ringMap, key, memo);}// no memo dfs, too slowint dfs(int tar, int markPos, unordered_map<char, vector<int>>& ringMap, string& key) {if(tar == key.length()) {return 0;}int minStep = INT_MAX;// for cur key char, try ervery possible way on the ringfor(auto tarPos : ringMap[key[tar]]) {int curStep = minDis(tarPos, markPos); // rotatecurStep += 1; // writeminStep = min(minStep,dfs(tar + 1, tarPos, ringMap, key) + curStep);}return minStep;}// memo versionint dfsMemo(int tar, int markPos, unordered_map<char, vector<int>>& ringMap, string& key,vector<vector<int>>& memo) {if(tar == key.length()) {return 0;}if(INT_MAX != memo[tar][markPos]) {return memo[tar][markPos];}// for cur key char, try ervery possible way on the ringfor(auto tarPos : ringMap[key[tar]]) {int curStep = minDis(tarPos, markPos); // rotatecurStep += 1; // writememo[tar][markPos] = min(memo[tar][markPos],dfsMemo(tar + 1, tarPos, ringMap, key, memo) + curStep);}// cout << "memo ing: " << tar << ", " << markPos << ": " << memo[tar][markPos] << endl;return memo[tar][markPos];}int minDis(int tarPos, int markPos) {int gt = max(tarPos, markPos);int lt = min(tarPos, markPos);return min(gt - lt, lt + ringLen - gt);}
};

0968minCameraCover 监控二叉树

1 题目

https://leetcode-cn.com/problems/binary-tree-cameras/submissions/

2 解题思路

  • 1 首先得在思考的过程中,理解改题目的本质就是,

    • 1.1 在dfs的过程中,在每个节点可以放置或者不放置相机,重要的是,如何去体现放置还是不放置相机
    • 1.2 如何提现呢?很简单,比如root放置,然后你想让它的两个子节点都不放置,那么直接递归调用两个子节点的后代即可,具体看代码即可
    • 1.3 那么对于每个节点,有几种放置相机的可能呢?
      • 一共三种,要么root,要么left,要么right,然后取最小代价即可,这里以选择left子节点仔细说明:
        // choosing: right int tmpRightCover = min(// r的监控器对r的两个子节点都有监控作用,于是直接去计算两个子节点的子节点1 + minCameraCover(r_rll) + minCameraCover(r_rlr) + minCameraCover(r_rrl) + minCameraCover(r_rrr) + minCameraCover(r_l),min(// r的监控器对r的两个子节点中的右孩子有监控作用,于是计算方式变为算右孩子两个子节点加上左侧节点// partly ignore, will not put cam on r_rr, may on r_r1 + minCameraCover(r_rrl) + minCameraCover(r_rrr) + minCameraCover(r_rl) + minCameraCover(r_l),// r的监控器对r的两个子节点中的左孩子有监控作用,于是计算方式变为算左孩子两个子节点加上右侧节点// partly ignore, will not put cam on r_rl, may on r_l1 + minCameraCover(r_rll) + minCameraCover(r_rlr) + minCameraCover(r_rr) + minCameraCover(r_l)));
  • 2 优化思路:

    • 2.1 同一层递归里面,相同函数名不要反复出现,用临时变量存储以加速
    • 2.2 使用hash存储对应的节点的最小监控值,如果能在hash命中就不用反复计算
    • 2.3 优先计算小规模,然后计算大规模
  • 3 关于为什么需要hash来避免反复计算:
    • 3.1 看例子:
      [0,null,0,null,0,null,0,null,0,null,0,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,0,null,null,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null,0,null]
    • 3.2 也就是单链表,你会发现,到达第4个节点,可以有两种监控方式,那么说明第4个节点的计算会重复2次,于是需要记忆化搜索
/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {public:int level = 0;unordered_map<TreeNode* , int> memo;int minCameraCover(TreeNode* root) {++level;if(nullptr == root) {return 0;}if(nullptr == root->left && nullptr == root->right) {return 1;}// dp:TreeNode* r_l = root->left;TreeNode* r_r = root->right;TreeNode* r_ll = getL(r_l);TreeNode* r_lr = getR(r_l);TreeNode* r_rl = getL(r_r);TreeNode* r_rr = getR(r_r);TreeNode* r_lll = getL(r_ll);TreeNode* r_llr = getR(r_ll);TreeNode* r_lrl = getL(r_lr);TreeNode* r_lrr = getR(r_lr);TreeNode* r_rll = getL(r_rl);TreeNode* r_rlr = getR(r_rl);TreeNode* r_rrl = getL(r_rr);TreeNode* r_rrr = getR(r_rr);int cover_r_lll = getFromMemo(r_lll);int cover_r_llr = getFromMemo(r_llr);int cover_r_lrl = getFromMemo(r_lrl);int cover_r_lrr = getFromMemo(r_lrr);int cover_r_rll = getFromMemo(r_rll);int cover_r_rlr = getFromMemo(r_rlr);int cover_r_rrl = getFromMemo(r_rrl);int cover_r_rrr = getFromMemo(r_rrr);int cover_r_ll = getFromMemo(r_ll);int cover_r_lr = getFromMemo(r_lr);int cover_r_rl = getFromMemo(r_rl);int cover_r_rr = getFromMemo(r_rr);int cover_r_l = getFromMemo(r_l);int cover_r_r = getFromMemo(r_r);// // choosing: root// int tmpRootCover = min(//     // min(//         // do not ignore//     1 + minCameraCover(r_ll) + minCameraCover(r_lr) + minCameraCover(r_rl) + minCameraCover(r_rr),//         // 1 + minCameraCover(r_l) + minCameraCover(r_r)//     // ),//     // partly ignore root choosen//     min(//         1 + minCameraCover(r_ll) + minCameraCover(r_lr) + minCameraCover(r_r), //         1 + minCameraCover(r_rl) + minCameraCover(r_rr) + minCameraCover(r_l)//     )// );int tmpRootCover = min(1 + cover_r_ll + cover_r_lr + cover_r_rl + cover_r_rr,min(1 + cover_r_ll + cover_r_lr + cover_r_r,1 + cover_r_rl + cover_r_rr + cover_r_l));// // choosing: right // int tmpRightCover = min(//     // don't ignore, will not put cam on r_rl, r_rr//     1 + minCameraCover(r_rll) + minCameraCover(r_rlr) + minCameraCover(r_rrl) + minCameraCover(r_rrr) + minCameraCover(r_l),//     min(//         // partly ignore, will not put cam on r_rr, may on r_r//         1 + minCameraCover(r_rrl) + minCameraCover(r_rrr) + minCameraCover(r_rl) + minCameraCover(r_l),//         // partly ignore, will not put cam on r_rl, may on r_l//         1 + minCameraCover(r_rll) + minCameraCover(r_rlr) + minCameraCover(r_rr) + minCameraCover(r_l)//     )// );int tmpRightCover = min(1 + cover_r_rll + cover_r_rlr + cover_r_rrl + cover_r_rrr + cover_r_l,min(1 + cover_r_rrl + cover_r_rrr + cover_r_rl + cover_r_l,1 + cover_r_rll + cover_r_rlr + cover_r_rr +  cover_r_l));// // choosing: left// int tmpLeftCover = min(//     1 + minCameraCover(r_lll) + minCameraCover(r_llr) + minCameraCover(r_lrl) + minCameraCover(r_lrr) + minCameraCover(r_r),//     min(//         1 + minCameraCover(r_ll) + minCameraCover(r_lrl) + minCameraCover(r_lrr) + minCameraCover(r_r),//         1 + minCameraCover(r_lr) + minCameraCover(r_lll) + minCameraCover(r_llr)  + minCameraCover(r_r)//     )// );int tmpLeftCover = min(1 + cover_r_lll + cover_r_llr + cover_r_lrl + cover_r_lrr + cover_r_r,min(1 + cover_r_ll + cover_r_lrl + cover_r_lrr + cover_r_r,1 + cover_r_lr + cover_r_lll + cover_r_llr + cover_r_r));return min(tmpRootCover, min(tmpLeftCover, tmpRightCover));}TreeNode* getR(TreeNode* root) {if(nullptr == root) {return nullptr;} else {return root->right;}}TreeNode* getL(TreeNode* root) {if(nullptr == root) {return nullptr;} else {return root->left;}}int getFromMemo(TreeNode* root) {if(memo.end() == memo.find(root)) {memo[root] = minCameraCover(root);} return memo[root];}};

lcDFS1 深度优先遍历1[记忆化搜索]相关推荐

  1. 动态规划+深度优先搜索+记忆化搜索(干货满满)

    动态规划的定义 动态规划算法与分治法的思想类似,都是通过组合子问题的解来求原问题.我先来给大家介绍一下分治思想,分治思想就是把一个复杂的问题,分解为k个规模相同的子问题,如果还是无法解决,子问题又可以 ...

  2. 常用的递归算法:dfs(深度优先搜索),记忆化搜索,分治

    递归值函数调用自身: 一.DFS的介绍 概念很简单,但是还是得了解一下,下面介绍的内容是重点 1. 图的DFS (1)先看一下vector是怎么建图的? vector<int> vecMa ...

  3. 搜索问题之状态空间搜索(状态压缩+记忆化搜索+ BFS)

    文章目录 1. 前言 2. 问题举例(九宫格问题) 3. 问题分析 3.1 状态编码与解码 3.2 哈希映射 3.3 集合判重 4. 问题实现 推荐阅读 1. 前言 之前介绍的回溯法常用于 解空间的搜 ...

  4. 中石油训练赛 - The King’s Ups and Downs(记忆化搜索)

    题目描述 The king has guards of all different heights. Rather than line them up in increasing or decreas ...

  5. FatMouse and Cheese HDU - 1078(记忆化搜索入门模板)

    题意: n * n的正方形格子(每个格子均放了奶酪),老鼠从(0,0)开始,每次最多移动k步,可以选择上下左右四个方向移动,下一个移动点奶酪块数量必须要大于当前点. 整理模板ing- 题目: FatM ...

  6. [Leetcode][第312题][JAVA][戳气球][动态规划][记忆化搜索]

    [问题描述][困难] [解答思路] 1. 记忆化搜索 时间复杂度:O(n^3) 空间复杂度:O(n^2) class Solution {public int[][] rec;public int[] ...

  7. 巧用记忆化搜索代替暴力递归(洛谷P1464题题解,Java语言描述)

    题目要求 P1464题目链接 分析 如果--你信了这题干,真的写了递归--TLE警告!!! 所以,就需要优化嘛-- [−9223372036854775808,9223372036854775807] ...

  8. 记忆化搜索=搜索的形式+动态规划的思想(来自百度百科)

    记忆化搜索=搜索的形式+动态规划的思想 记忆化搜索:算法上依然是搜索的流程,但是搜索到的一些解用动态规划的那种思想和模式作一些保存. 一般说来,动态规划总要遍历所有的状态,而搜索可以排除一些无效状态. ...

  9. HDU2089 不要62【数位DP+记忆化搜索】

    不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submi ...

最新文章

  1. 算法分析之-主方法分析递归式
  2. 1分钟快速生成用于网页内容提取的xslt
  3. 新手必看 | RVB2601开发板快速上手指南
  4. win7为什么打开桌面上的计算机很卡很慢,Win7系统运行不流畅卡半天才进入桌面的六种解决方法...
  5. 【bzoj4868】[Shoi2017]期末考试 前缀和+暴力
  6. 解析java程序设计第二版答案,解析Java程序设计答案
  7. Mac查看本机公网IP
  8. pdf用什么软件打开
  9. Kod – 程序员专用编辑器[Mac]
  10. java fop_java – 未找到类FOP
  11. 中线提取算法_基于Guided Filter的地形图中线要素提取算法
  12. Qt 局域网聊天(功能完善,界面美观,免费下载)
  13. latex输入3.3.1类型的标题
  14. AVL树的操作及调整成AVL树
  15. 百度霸屏引流推广需要多少钱?那么才能霸屏?百度霸屏有什么好处?
  16. PSpice受控源设置增益参数
  17. 三天搞定射频识别技术(二)2.4 S50卡原理
  18. 解决ontop连接h2数据库,出现数据库已连接的问题
  19. Python实现comtrade文件读取
  20. 小小新闻发布会资料_小小新闻发布会

热门文章

  1. 关于虚拟机镜像无法检测
  2. 【STM32】STM32 OLED打点划线画圆 OLED电子罗盘 程序
  3. Qt 之 QVideoFrame转换为QImage
  4. 如何去使用Python分析股票数据?学到就是赚到
  5. 人工智能科技成熟的11个Github上免费开源项目,很多电影中才有的场景应用到现实颠覆普通人的认知和想象
  6. 38外观数列(循环遍历)
  7. 北邮网研院宿舍_北邮研究生宿舍条件(北邮研究生宿舍图片)
  8. 无线网络技术【认识了解】
  9. 杭州端点网络java开发实习生笔试题自我反省
  10. python将数据逐行写入excel_使用python将数据写入excel