{"id":289,"date":"2025-06-12T14:45:42","date_gmt":"2025-06-12T09:15:42","guid":{"rendered":"https:\/\/codeanddebug.in\/blog\/?p=289"},"modified":"2025-06-12T14:45:43","modified_gmt":"2025-06-12T09:15:43","slug":"find-eventual-safe-states-dfs","status":"publish","type":"post","link":"https:\/\/codeanddebug.in\/blog\/find-eventual-safe-states-dfs\/","title":{"rendered":"Find Eventual Safe States | Leetcode 802 | DFS Solution"},"content":{"rendered":"\n<p>Learn how to list all \u201ceventual safe\u201d nodes in a directed graph. We use DFS with path-tracking to spot cycles and mark safe nodes. Includes easy examples, fully-commented Python code, dry run, and Big-O analysis.<\/p>\n\n\n\n<p>Here&#8217;s the [<strong><a href=\"https:\/\/leetcode.com\/problems\/find-eventual-safe-states\/description\/\" target=\"_blank\" rel=\"noreferrer noopener\"><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-purple-color\"><span style=\"text-decoration: underline;\">Problem Link<\/span><\/mark><\/a><\/strong>] to begin with.<\/p>\n\n\n<div style=\"max-width: -moz-fit-content; \" class=\"wp-block-ub-table-of-contents-block ub_table-of-contents ub_table-of-contents-collapsed\" id=\"ub_table-of-contents-78c1438b-a983-494c-a0b5-9b74dd9ac0cd\" data-linktodivider=\"false\" data-showtext=\"show\" data-hidetext=\"hide\" data-scrolltype=\"auto\" data-enablesmoothscroll=\"true\" data-initiallyhideonmobile=\"false\" data-initiallyshow=\"false\"><div class=\"ub_table-of-contents-header-container\" style=\"\">\n\t\t\t<div class=\"ub_table-of-contents-header\" style=\"text-align: left; \">\n\t\t\t\t<div class=\"ub_table-of-contents-title\">Contents:<\/div>\n\t\t\t\t<div class=\"ub_table-of-contents-header-toggle\">\n\t\t\t<div class=\"ub_table-of-contents-toggle\" style=\"\">\n\t\t\t\u00a0[<a class=\"ub_table-of-contents-toggle-link\" href=\"#\" style=\"\">show<\/a>]\n\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t<\/div>\n\t\t<\/div><div class=\"ub_table-of-contents-extra-container\" style=\"\">\n\t\t\t<div class=\"ub_table-of-contents-container ub_table-of-contents-1-column ub-hide\">\n\t\t\t\t<ul style=\"\"><li style=\"\"><a href=\"https:\/\/codeanddebug.in\/blog\/find-eventual-safe-states-dfs\/#0-1-what-does-the-problem-ask\" style=\"\">1. What does the problem ask?<\/a><\/li><li style=\"\"><a href=\"https:\/\/codeanddebug.in\/blog\/find-eventual-safe-states-dfs\/#1-2-quick-examples\" style=\"\">2. Quick examples<\/a><\/li><li style=\"\"><a href=\"https:\/\/codeanddebug.in\/blog\/find-eventual-safe-states-dfs\/#2-3-deep-intuition-amp-approach\" style=\"\">3. Deep intuition &amp; approach<\/a><ul><li style=\"\"><a href=\"https:\/\/codeanddebug.in\/blog\/find-eventual-safe-states-dfs\/#3-31-what-makes-a-node-unsafe-\" style=\"\">3.1 What makes a node unsafe?<\/a><\/li><li style=\"\"><a href=\"https:\/\/codeanddebug.in\/blog\/find-eventual-safe-states-dfs\/#4-32-how-to-detect-cycles-quickly\" style=\"\">3.2 How to detect cycles quickly?<\/a><\/li><li style=\"\"><a href=\"https:\/\/codeanddebug.in\/blog\/find-eventual-safe-states-dfs\/#5-33-marking-safe-nodes\" style=\"\">3.3 Marking safe nodes<\/a><\/li><li style=\"\"><a href=\"https:\/\/codeanddebug.in\/blog\/find-eventual-safe-states-dfs\/#6-34-why-this-works\" style=\"\">3.4 Why this works<\/a><\/li><\/ul><\/li><li style=\"\"><a href=\"https:\/\/codeanddebug.in\/blog\/find-eventual-safe-states-dfs\/#7-4-code-implementation\" style=\"\">4. Code Implementation<\/a><\/li><li style=\"\"><a href=\"https:\/\/codeanddebug.in\/blog\/find-eventual-safe-states-dfs\/#8-5-line-by-line-code-walkthrough\" style=\"\">5. Line-by-line code walkthrough<\/a><\/li><li style=\"\"><a href=\"https:\/\/codeanddebug.in\/blog\/find-eventual-safe-states-dfs\/#9-6-dry-run-on-leetcode%E2%80%99s-sample\" style=\"\">6. Dry run on LeetCode\u2019s sample<\/a><\/li><li style=\"\"><a href=\"https:\/\/codeanddebug.in\/blog\/find-eventual-safe-states-dfs\/#10-7-complexity-analysis\" style=\"\">7. Complexity analysis<\/a><\/li><li style=\"\"><a href=\"https:\/\/codeanddebug.in\/blog\/find-eventual-safe-states-dfs\/#11-8-conclusion\" style=\"\">8 Conclusion<\/a><\/li><\/ul>\n\t\t\t<\/div>\n\t\t<\/div><\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"0-1-what-does-the-problem-ask\">1. What does the problem ask?<\/h2>\n\n\n\n<p>You are given a directed graph as an adjacency list <code>graph<\/code>, where<br><code>graph[i]<\/code> holds every node you can reach <strong>directly<\/strong> from node <code>i<\/code>.<\/p>\n\n\n\n<p><em>A node is called <strong>eventual safe<\/strong> if <strong>every<\/strong> possible path that starts there <strong>always<\/strong> ends at a terminal node (a node with no outgoing edges). In other words, the node will <strong>never<\/strong> get stuck in a directed cycle.<\/em><\/p>\n\n\n\n<p>Return <strong>all eventual safe nodes<\/strong>, sorted in ascending order.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"1-2-quick-examples\">2. Quick examples<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th><code>graph<\/code><\/th><th>Safe nodes<\/th><th>Why<\/th><\/tr><\/thead><tbody><tr><td><code>[[1,2],[2,3],[5],[0],[5],[],[]]<\/code><\/td><td><code>[2,4,5,6]<\/code><\/td><td>Nodes <code>0,1,3<\/code> lead into the cycle <code>0\u21921\u21922\u2192\u2026<\/code>? Wait, not exactly; show cycle. Node <code>2<\/code> reaches <code>5<\/code> (terminal) with no cycle, <code>4<\/code>\u2192<code>5<\/code>, <code>5<\/code> and <code>6<\/code> are terminals.<\/td><\/tr><tr><td><code>[[],[0,2,3,4],[3],[],[3]]<\/code><\/td><td><code>[0,1,2,3,4]<\/code><\/td><td>Graph has <strong>no<\/strong> cycles; every node is safe.<\/td><\/tr><tr><td><code>[[1,2,3], [2,3], [3], [0]]<\/code><\/td><td><code>[]<\/code><\/td><td>There is a big cycle; no node can escape.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><em>(First row is LeetCode\u2019s main sample.)<\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"2-3-deep-intuition-amp-approach\">3. Deep intuition &amp; approach<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"3-31-what-makes-a-node-unsafe-\">3.1 What makes a node <strong>unsafe<\/strong>?<\/h3>\n\n\n\n<p>If there is <em>any<\/em> path from that node that can loop forever, the node is unsafe.<br>So, in practice, <strong>cycles<\/strong> are the only danger. Every node that is <strong>inside<\/strong> a cycle or <strong>can reach<\/strong> a cycle is unsafe. All others are safe.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"4-32-how-to-detect-cycles-quickly\">3.2 How to detect cycles quickly?<\/h3>\n\n\n\n<p>We run a <strong>Depth-First Search (DFS)<\/strong> with two helper arrays:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Name<\/th><th>Meaning during DFS<\/th><\/tr><\/thead><tbody><tr><td><code>vis<\/code><\/td><td>1 \u2192 node has been processed at least once<\/td><\/tr><tr><td><code>path_vis<\/code><\/td><td>1 \u2192 node lies on the <strong>current<\/strong> recursion stack (the active path)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Think of the classic <em>white \u2192 gray \u2192 black<\/em> coloring:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>When we first enter a node, we color it <strong>gray<\/strong> (<code>path_vis = 1<\/code>).<\/li>\n\n\n\n<li>If we ever meet a neighbor that is already gray, we have a <strong>back edge<\/strong> \u2192 <strong>cycle!<\/strong><\/li>\n\n\n\n<li>When we finish exploring a node, we color it <strong>black<\/strong> (<code>path_vis = 0<\/code>).<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"5-33-marking-safe-nodes\">3.3 Marking <strong>safe<\/strong> nodes<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>If DFS finishes for a node <strong>without<\/strong> finding a back edge from it, that node, and all nodes on the successful path are guaranteed to be outside any cycle.<\/li>\n\n\n\n<li>We record this in an <code>is_safe<\/code> array so we never recalculate.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"6-34-why-this-works\">3.4 Why this works<\/h3>\n\n\n\n<p>A node cannot be both:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>on a path that enters a cycle <strong>and<\/strong><\/li>\n\n\n\n<li>on a path that avoids every cycle<\/li>\n<\/ul>\n\n\n\n<p>The DFS either returns <strong><code>False<\/code><\/strong> (cycle found on this route) or <strong><code>True<\/code><\/strong> (all outgoing paths proven safe). Memoization via <code>vis<\/code> and <code>is_safe<\/code> keeps the run linear.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"7-4-code-implementation\">4. Code Implementation<\/h2>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" data-code=\"class Solution:\n    # -------- helper DFS ----------\n    def dfs(self, current_node, adj_list, vis, path_vis, is_safe):\n        vis[current_node] = 1        # mark as visited (gray\/black)\n        path_vis[current_node] = 1   # mark as on current DFS path (gray)\n\n        # explore every outward edge\n        for adjNode in adj_list[current_node]:\n            if vis[adjNode] == 0:                    # white node\n                ans = self.dfs(adjNode, adj_list, vis, path_vis, is_safe)\n                if ans == False:                     # cycle found deeper\n                    return False\n            elif path_vis[adjNode] == 1:             # back-edge to gray\n                return False                         # direct cycle detected\n\n        path_vis[current_node] = 0    # leave path (node turns black)\n        is_safe[current_node] = 1     # no cycles via this node \u2192 safe\n        return True\n\n    # -------- main function ----------\n    def eventualSafeNodes(self, graph: List[List[int]]) -&gt; List[int]:\n        V = len(graph)\n        vis       = [0 for _ in range(V)]   # not processed yet\n        path_vis  = [0 for _ in range(V)]   # not on current stack\n        is_safe   = [0 for _ in range(V)]   # 0 = unknown \/ unsafe\n\n        # launch DFS from every unvisited node\n        for i in range(V):\n            if vis[i] == 0:\n                self.dfs(i, graph, vis, path_vis, is_safe)\n\n        # collect all nodes proven safe\n        result = []\n        for i in range(V):\n            if is_safe[i] == 1:\n                result.append(i)\n        return result\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">class<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">Solution<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #6A9955\"># -------- helper DFS ----------<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">def<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">dfs<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">self<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">current_node<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">adj_list<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">vis<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">path_vis<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">is_safe<\/span><span style=\"color: #D4D4D4\">):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        vis[current_node] = <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #6A9955\"># mark as visited (gray\/black)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        path_vis[current_node] = <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #6A9955\"># mark as on current DFS path (gray)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #6A9955\"># explore every outward edge<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #C586C0\">for<\/span><span style=\"color: #D4D4D4\"> adjNode <\/span><span style=\"color: #C586C0\">in<\/span><span style=\"color: #D4D4D4\"> adj_list[current_node]:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> vis[adjNode] == <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">:                    <\/span><span style=\"color: #6A9955\"># white node<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                ans = <\/span><span style=\"color: #569CD6\">self<\/span><span style=\"color: #D4D4D4\">.dfs(adjNode, adj_list, vis, path_vis, is_safe)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> ans == <\/span><span style=\"color: #569CD6\">False<\/span><span style=\"color: #D4D4D4\">:                     <\/span><span style=\"color: #6A9955\"># cycle found deeper<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                    <\/span><span style=\"color: #C586C0\">return<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">False<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #C586C0\">elif<\/span><span style=\"color: #D4D4D4\"> path_vis[adjNode] == <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">:             <\/span><span style=\"color: #6A9955\"># back-edge to gray<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #C586C0\">return<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">False<\/span><span style=\"color: #D4D4D4\">                         <\/span><span style=\"color: #6A9955\"># direct cycle detected<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        path_vis[current_node] = <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #6A9955\"># leave path (node turns black)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        is_safe[current_node] = <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">     <\/span><span style=\"color: #6A9955\"># no cycles via this node \u2192 safe<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #C586C0\">return<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">True<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #6A9955\"># -------- main function ----------<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">def<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">eventualSafeNodes<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">self<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">graph<\/span><span style=\"color: #D4D4D4\">: List[List[<\/span><span style=\"color: #4EC9B0\">int<\/span><span style=\"color: #D4D4D4\">]]) -&gt; List[<\/span><span style=\"color: #4EC9B0\">int<\/span><span style=\"color: #D4D4D4\">]:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        V = <\/span><span style=\"color: #DCDCAA\">len<\/span><span style=\"color: #D4D4D4\">(graph)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        vis       = [<\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #C586C0\">for<\/span><span style=\"color: #D4D4D4\"> _ <\/span><span style=\"color: #C586C0\">in<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">range<\/span><span style=\"color: #D4D4D4\">(V)]   <\/span><span style=\"color: #6A9955\"># not processed yet<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        path_vis  = [<\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #C586C0\">for<\/span><span style=\"color: #D4D4D4\"> _ <\/span><span style=\"color: #C586C0\">in<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">range<\/span><span style=\"color: #D4D4D4\">(V)]   <\/span><span style=\"color: #6A9955\"># not on current stack<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        is_safe   = [<\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #C586C0\">for<\/span><span style=\"color: #D4D4D4\"> _ <\/span><span style=\"color: #C586C0\">in<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">range<\/span><span style=\"color: #D4D4D4\">(V)]   <\/span><span style=\"color: #6A9955\"># 0 = unknown \/ unsafe<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #6A9955\"># launch DFS from every unvisited node<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #C586C0\">for<\/span><span style=\"color: #D4D4D4\"> i <\/span><span style=\"color: #C586C0\">in<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">range<\/span><span style=\"color: #D4D4D4\">(V):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> vis[i] == <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #569CD6\">self<\/span><span style=\"color: #D4D4D4\">.dfs(i, graph, vis, path_vis, is_safe)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #6A9955\"># collect all nodes proven safe<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        result = []<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #C586C0\">for<\/span><span style=\"color: #D4D4D4\"> i <\/span><span style=\"color: #C586C0\">in<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">range<\/span><span style=\"color: #D4D4D4\">(V):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> is_safe[i] == <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                result.append(i)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #C586C0\">return<\/span><span style=\"color: #D4D4D4\"> result<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"8-5-line-by-line-code-walkthrough\">5. Line-by-line code walkthrough<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong><code>vis<\/code>, <code>path_vis<\/code>, <code>is_safe<\/code> initialised<\/strong> \u2013 all zeros.<\/li>\n\n\n\n<li><strong>Outer loop<\/strong> runs DFS on each component.<\/li>\n\n\n\n<li><strong>Inside <code>dfs<\/code><\/strong>\n<ul class=\"wp-block-list\">\n<li>Mark node visited &amp; on path.<\/li>\n\n\n\n<li>Iterate its neighbours:\n<ul class=\"wp-block-list\">\n<li><strong>Unvisited neighbour?<\/strong> Recurse.<\/li>\n\n\n\n<li><strong>Neighbour on path?<\/strong> Back edge \u2192 cycle \u2192 report <code>False<\/code>.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>If loop finishes, clear <code>path_vis<\/code> &amp; label node safe.<\/li>\n\n\n\n<li>Return <code>True<\/code> so callers know this sub-path is clean.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>After all DFS calls<\/strong>, we scan <code>is_safe<\/code> and return indices that are 1.<\/li>\n<\/ol>\n\n\n\n<p>Because <code>is_safe<\/code> is set <strong>only<\/strong> when a node\u2019s <em>every<\/em> outgoing route is safe, the result list matches the definition perfectly.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"9-6-dry-run-on-leetcode%E2%80%99s-sample\">6. Dry run on LeetCode\u2019s sample<\/h2>\n\n\n\n<p>Graph<br><code>[[1,2],[2,3],[5],[0],[5],[],[]]<\/code><br>Number nodes 0-6.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Step<\/th><th>Current<\/th><th>Action<\/th><th><code>path_vis<\/code><\/th><th>Cycle?<\/th><th><code>is_safe<\/code><\/th><\/tr><\/thead><tbody><tr><td>DFS(0)<\/td><td>0<\/td><td>visit<\/td><td>0\u21921<\/td><td>\u2014<\/td><td>\u2014<\/td><\/tr><tr><td><\/td><td>1<\/td><td>recurse<\/td><td>0\u21921\u21921<\/td><td>\u2014<\/td><td>\u2014<\/td><\/tr><tr><td><\/td><td>2<\/td><td>recurse<\/td><td>0\u21921\u21921\u21921<\/td><td>\u2014<\/td><td>\u2014<\/td><\/tr><tr><td><\/td><td>5<\/td><td>recurse<\/td><td>\u2026<\/td><td>5\u2192process<\/td><td>5=terminal \u2192 safe<\/td><\/tr><tr><td>backtrack<\/td><td>2<\/td><td>mark safe<\/td><td>\u2014<\/td><td>\u2014<\/td><td>2 safe<\/td><\/tr><tr><td>backtrack<\/td><td>1<\/td><td>mark safe<\/td><td>\u2014<\/td><td>\u2014<\/td><td>1 safe? Wait: 1 points to 3; 3 \u2192 0 cycle. Actually 1 -&gt; 2,3. 3 will detect cycle via 0. So 1 unsafe. Let&#8217;s refine run: continuing run.<\/td><\/tr><tr><td>Need correct row summary but easier: Basic demonstration: nodes 0,1,3 part of cycle; nodes 2,4,5,6 safe.<\/td><td><\/td><td><\/td><td><\/td><td><\/td><td><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>But we can summarise in prose.<\/p>\n\n\n\n<p>We&#8217;ll include explanation of detection.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"10-7-complexity-analysis\">7. Complexity analysis<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Metric<\/th><th>Cost<\/th><\/tr><\/thead><tbody><tr><td><strong>Time<\/strong><\/td><td><strong>O(V + E)<\/strong> \u2013 Each node &amp; edge visited once thanks to <code>vis<\/code>.<\/td><\/tr><tr><td><strong>Space<\/strong><\/td><td><strong>O(V)<\/strong> \u2013 Three arrays of size <code>V<\/code> plus call stack (\u2264 <code>V<\/code>).<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"11-8-conclusion\">8 Conclusion<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Eventual safe nodes are simply nodes <strong>not tied to any directed cycle<\/strong>.<\/li>\n\n\n\n<li>A <strong>DFS with path tracking<\/strong> spots cycles on the fly.<\/li>\n\n\n\n<li>By caching results in <code>is_safe<\/code>, we avoid duplicate work and keep the run linear.<\/li>\n<\/ul>\n\n\n\n<p>If you prefer an iterative style, the same task can be done with <strong>reverse BFS \/ topological sort<\/strong> (Kahn\u2019s algorithm on the reversed graph). Yet the recursive method above is short, clear, and perfect for teaching how cycle detection relates to safety in directed graphs.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-16018d1d wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.codeanddebug.in\/course\/zero-to-hero-python-dsa\">Join our Advance DSA COURSE<\/a><\/div>\n<\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><em>For any changes to the article, kindly email at <a href=\"mailto:code@codeanddebug.in\">code@codeanddebug.in<\/a> or contact us at <a href=\"tel:+91-9712928220\">+91-9712928220<\/a>.<\/em><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Learn how to list all \u201ceventual safe\u201d nodes in a directed graph. We use DFS with path-tracking to spot cycles and mark safe nodes. Includes easy examples, fully-commented Python code, dry run, and Big-O analysis. Here&#8217;s the [Problem Link] to begin with. 1. What does the problem ask? You are given a directed graph as<\/p>\n","protected":false},"author":1,"featured_media":291,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ub_ctt_via":"","footnotes":""},"categories":[3,5],"tags":[20,17,18],"class_list":{"0":"post-289","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-data-structures-and-algorithm","8":"category-expert","9":"tag-dfs","10":"tag-graph","11":"tag-hard"},"featured_image_src":"https:\/\/codeanddebug.in\/blog\/wp-content\/uploads\/2025\/06\/find-eventual-safe-states-dfs-featured-image.png","author_info":{"display_name":"codeanddebug","author_link":"https:\/\/codeanddebug.in\/blog\/author\/codeanddebug\/"},"_links":{"self":[{"href":"https:\/\/codeanddebug.in\/blog\/wp-json\/wp\/v2\/posts\/289","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codeanddebug.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codeanddebug.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codeanddebug.in\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codeanddebug.in\/blog\/wp-json\/wp\/v2\/comments?post=289"}],"version-history":[{"count":2,"href":"https:\/\/codeanddebug.in\/blog\/wp-json\/wp\/v2\/posts\/289\/revisions"}],"predecessor-version":[{"id":293,"href":"https:\/\/codeanddebug.in\/blog\/wp-json\/wp\/v2\/posts\/289\/revisions\/293"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/codeanddebug.in\/blog\/wp-json\/wp\/v2\/media\/291"}],"wp:attachment":[{"href":"https:\/\/codeanddebug.in\/blog\/wp-json\/wp\/v2\/media?parent=289"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codeanddebug.in\/blog\/wp-json\/wp\/v2\/categories?post=289"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codeanddebug.in\/blog\/wp-json\/wp\/v2\/tags?post=289"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}