楼主: flykite083
跳转到指定楼层
上一主题 下一主题
收起左侧

[树/链表/图] 求教Airbnb的graph traversal问题

🔗
ericliu03 2019-3-21 07:14:36 | 只看该作者
全局:
mren 发表于 2019-3-19 11:37
0 -> 1
1 -> 0
的话是一个环,首先访问0,然后DFS的时候会访问到1,然后当继续DFS的时候访问0,但是0被 ...

我觉得这里有两个问题(我理解的ret是存start node in a SCC)
1:在line 21你先把i加到了ret 里面,在你描述的从1访问0的情况下,在line 4这个在ret 里面的0,会先被删掉,然后才检查是不是要继续dfs。这个就是seaskymonster 说的结果是0的情况

2. 假设我们 先dfs 再 把这个start node (0) 加入到ret里面。那么比如这个例子,0 <-> 1 <- 2
如果从0开始,那么就是 0 -> 1, 然后 1->0发现 0 已经visited ,所以退出,那么visited = (0, 1) ret = (0)
但是当从2开始时,因为1已经在visited里面了所以不会走到0,也就不会去删除在ret里面的0,所以结果为2,也是不对



根据这个思路,需要解决以上两个问题 1. 不能把current start node自己删了,2. 能够可以访问到以前的start_node然后可以删除掉
解决1其实就是先看以当前v为start,能dfs到哪些点,包括start自己,然后删掉出现在ret里面的,也就是以前的start node(已经被最新的覆盖)最后删除完了,再把自己加上
解决2需要保证以当前v为start的时候,即使碰到了之前visit 过的scc,我们也要进去走一圈,看看哪个是以前的start

这个解法我试了试几个test case结果都是对的,大家给分析一下?
我用python写的代码贴在下面
您好!
本帖隐藏的内容需要积分高于 110 才可浏览
您当前积分为 0。
使用VIP即刻解锁阅读权限或查看其他获取积分的方式
游客,您好!
本帖隐藏的内容需要积分高于 110 才可浏览
您当前积分为 0。
VIP即刻解锁阅读权限 或 查看其他获取积分的方式
Popular Interview Questions from Top Companies

回复

使用道具 举报

🔗
14417335 2019-3-21 07:23:26 | 只看该作者
全局:
ericliu03 发表于 2019-3-21 07:14
我觉得这里有两个问题(我理解的ret是存start node in a SCC)
1:在line 21你先把i加到了ret 里面,在 ...

赞认真!请考虑楼上magicsets和其它几楼。
回复

使用道具 举报

🔗
nicemayi 2019-4-9 13:22:44 | 只看该作者
全局:
ericliu03 发表于 2019-3-21 07:14
我觉得这里有两个问题(我理解的ret是存start node in a SCC)
1:在line 21你先把i加到了ret 里面,在 ...

感觉你的解法是O(n)的,带剪枝并不是爆搜:)
对于这道题看到很多人推崇scc Kosaraju 看的也是挺懵逼的,难道这道题直接类似LC200中DFS的解法有什么问题吗?贴上了层主的解法加上楼主test case的一些变化,欢迎大家猛喷提出其他的test case:)
  1. from collections import defaultdict

  2. def dag_traverse(edges):
  3.     graph = defaultdict(list)
  4.     vertex = set()
  5.     for start, end in edges:
  6.         graph[start].append(end)
  7.         vertex.add(start)
  8.         vertex.add(end)

  9.     visited = set()
  10.     res = 0
  11.     for each in vertex:
  12.         if each not in visited:
  13.             visited.add(each)
  14.             res += 1
  15.             for child in graph[each]:
  16.                 dfs(child, graph, visited)
  17.    
  18.     return res

  19. def dfs(node, graph, visited):
  20.     if node in visited:
  21.         return
  22.     visited.add(node)
  23.     for each in graph[node]:
  24.         dfs(each, graph, visited)


  25. if __name__ == '__main__':

  26.     """
  27.     edge1:
  28.     1----->2--->3-->4
  29.     ^               |
  30.     |     8         |
  31.     |     ^         |
  32.     |     |         V
  33.     9<----7<----6<--5
  34.     """
  35.     edges1 = [
  36.         [1, 2],
  37.         [2, 3],
  38.         [3, 4],
  39.         [4, 5],
  40.         [5, 6],
  41.         [6, 7],
  42.         [7, 8],
  43.         [7, 9],
  44.         [9, 1],
  45.     ]
  46.     res1 = dag_traverse(edges1)
  47.     assert res1 == 1
  48.     print('*' * 40)

  49.     """
  50.     edge2:
  51.     1----->2--->3-->4
  52.     ^               |
  53.     |     8         |
  54.     |     ^         |
  55.     |     |         V
  56.     9<----7<----6   5
  57.     """
  58.     edges2 = [
  59.         [1, 2],
  60.         [2, 3],
  61.         [3, 4],
  62.         [4, 5],
  63.         # [5, 6],
  64.         [6, 7],
  65.         [7, 8],
  66.         [7, 9],
  67.         [9, 1],
  68.     ]
  69.     res2 = dag_traverse(edges2)
  70.     assert res2 == 2


  71.     """
  72.     edge3:
  73.     1----->2--->3-->4
  74.                     |
  75.           8         |
  76.           ^         |
  77.           |         V
  78.     9<----7<----6   5
  79.     """
  80.     edges3 = [
  81.         [1, 2],
  82.         [2, 3],
  83.         [3, 4],
  84.         [4, 5],
  85.         # [5, 6],
  86.         [6, 7],
  87.         [7, 8],
  88.         [7, 9],
  89.         # [9, 1],
  90.     ]
  91.     res3 = dag_traverse(edges3)
  92.     assert res3 == 2
复制代码
回复

使用道具 举报

🔗
ericliu03 2019-4-10 04:58:33 | 只看该作者
全局:
nicemayi 发表于 2019-4-9 13:22
感觉你的解法是O(n)的,带剪枝并不是爆搜:)
对于这道题看到很多人推崇scc Kosaraju 看的也是挺懵逼的, ...

hmm... 我看了一下,感觉是O(n2)的,在worst case下比如 A->B->C->D, 我们恰巧loop的顺序是从D开始,那么对于每个点我们都要作为起点开始,对于他所有可以到的点进行一次loop

原楼主给的解法是剪枝了的但是有些问题,我这好像并没有剪啊。。

另外我感觉面试的时候如果能条理清晰解释这个解法写出clean code感觉没有什么问题,如果写SCC那个,可能要求更高,包括代码量,解释量等等
回复

使用道具 举报

🔗
nicemayi 2019-4-10 10:28:30 | 只看该作者
全局:
ericliu03 发表于 2019-4-10 04:58
hmm... 我看了一下,感觉是O(n2)的,在worst case下比如 A->B->C->D, 我们恰巧loop的顺序是从D开始,那么 ...

对,我没想到过你给的这个case。visited我的理解就是剪枝了。
回复

使用道具 举报

🔗
nicemayi 2019-4-10 10:30:36 | 只看该作者
全局:
nicemayi 发表于 2019-4-10 10:28
对,我没想到过你给的这个case。visited我的理解就是剪枝了。

或者还有一种优化思路:从入度为0的点开始遍历,当然如果没有就说明有环,从任何点开始都会是O(n)的。trade off是牺牲一点空间复杂度。
回复

使用道具 举报

🔗
ericliu03 2019-4-11 11:30:19 | 只看该作者
全局:
nicemayi 发表于 2019-4-10 10:30
或者还有一种优化思路:从入度为0的点开始遍历,当然如果没有就说明有环,从任何点开始都会是O(n)的。tra ...

我想了想,这个肯定是要比彻底暴力要好的。比如,对于每个点都跑一遍dfs(或者bfs)找到相连的,然后一个recursion 再对没有visited的点来一遍,直到全部visit,有几层recursion就是有几个start,全部试一遍最少的就是答案。。

我觉得你说的优化确实能解决一些case,提高avg 复杂度,但是我感觉这类优化一般不能提高worst case的复杂度,比如一个情况:
1->0, 2->0, 3->0, 4->0,0->5->6->7->8,那么就是 n/2 * n/2 还是O(n2)?

感觉应该是可以用数学证明一下的,然而数学并不好0.0
回复

使用道具 举报

全局:
nicemayi 发表于 2019-4-9 13:22
感觉你的解法是O(n)的,带剪枝并不是爆搜:)
对于这道题看到很多人推崇scc Kosaraju 看的也是挺懵逼的, ...

为什么第三个case 结果是2不是3呢?

补充内容 (2019-5-2 15:18):
如果第三个case是2的话,那么第二个case不应该是1吗?从6号出发都能访问到呀?
回复

使用道具 举报

🔗
lc19890306 2019-6-9 07:04:57 | 只看该作者
全局:
维护所有点的入度,对有向图run一遍dfs,遇到圈就破圈,即如果当前点已经访问过且是在圈上则将其入度减一,最后得到一个DAG,找出所有入度为0的点即可
  1. #include <iostream>
  2. #include <unordered_map>
  3. #include <unordered_set>
  4. #include <vector>

  5. using namespace std;

  6. unordered_map<int, vector<int>> g;
  7. unordered_map<int, int> indeg, visited;

  8. void dfs(int u) {
  9.         if (visited[u] == 1) return;
  10.         if (visited[u] == -1) {
  11.                 --indeg[u];
  12.                 return;
  13.         }
  14.         visited[u] = -1;
  15.         for (int v : g[u]) {
  16.                 dfs(v);
  17.         }
  18.         visited[u] = 1;
  19. }

  20. int solution(const vector<vector<int>> &edges) {
  21.         unordered_set<int> nodes;
  22.         for (const auto &e : edges) {
  23.                 g[e[0]].push_back(e[1]);
  24.                 ++indeg[e[1]];
  25.                 nodes.insert(e[0]);
  26.                 nodes.insert(e[1]);
  27.         }
  28.         for (int x : nodes) {
  29.                 dfs(x);
  30.         }
  31.         int res = 0;
  32.         for (int x : nodes) {
  33.                 res += indeg[x] == 0;
  34.         }
  35.         return res;
  36. }

  37. int main() {
  38.         // your code goes here
  39.         vector<vector<int>> edges = {{1, 3}, {3, 2}, {2, 1}, {3, 4}, {2, 4}, {3, 5},
  40.                                                                 {5, 4}, {4, 6}, {6, 4}, {7, 4}, {7, 12}, {7, 8},
  41.                                                                 {8, 7}, {10, 9}, {11, 10}};
  42.         cout << solution(edges) << endl;
  43.         return 0;
  44. }
复制代码
回复

使用道具 举报

🔗
lc19890306 2019-6-9 07:11:50 | 只看该作者
全局:
nicemayi 发表于 2019-4-9 00:22
感觉你的解法是O(n)的,带剪枝并不是爆搜:)
对于这道题看到很多人推崇scc Kosaraju 看的也是挺懵逼的, ...

你的算法是错的,test case #2的结果应该是1,从点6开始可以达到所有其他点
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号
隐私提醒:
  • ☑ 禁止发布广告,拉群,贴个人联系方式:找人请去🔗同学同事飞友,拉群请去🔗拉群结伴,广告请去🔗跳蚤市场,和 🔗租房广告|找室友
  • ☑ 论坛内容在发帖 30 分钟内可以编辑,过后则不能删帖。为防止被骚扰甚至人肉,不要公开留微信等联系方式,如有需求请以论坛私信方式发送。
  • ☑ 干货版块可免费使用 🔗超级匿名:面经(美国面经、中国面经、数科面经、PM面经),抖包袱(美国、中国)和录取汇报、定位选校版
  • ☑ 查阅全站 🔗各种匿名方法

本版积分规则

>
快速回复 返回顶部 返回列表