📌  相关文章
📜  检查给定的排列是否是图的有效 DFS(1)

📅  最后修改于: 2023-12-03 15:40:36.451000             🧑  作者: Mango

介绍:检查给定的排列是否是图的有效 DFS

在图论中,DFS(深度优先搜索)是一种常见的遍历算法。给定一个图,从某个节点开始,DFS会将其所有邻居节点都遍历一遍,然后再遍历邻居节点的邻居节点,以此类推。因此,DFS遍历的顺序是深度优先的,先访问某个节点及其邻居节点的所有子节点,然后再回溯到父节点,继续遍历其它邻居节点。

在实际应用中,我们通常使用一个整数数组来表示DFS遍历的顺序,比如,如果有一个长度为$n$的数组$dfs$,其中$dfs[i]$表示第$i$个遍历到的节点的编号,那么$dfs$应该满足以下条件:

  1. $dfs[0]$为DFS的起点;
  2. 对于任意的$i\in[0,n-1]$,节点$dfs[i]$必须是节点$dfs[i-1]$的邻居节点或其祖先节点;
  3. 对于任意的$i\in[0,n-1]$,节点$dfs[i]$不能是节点$dfs[i+1]$的祖先节点。

我们要完成的任务是检查给定的排列是否是图的有效DFS序列。

思路

首先,我们需要将给定的排列还原为对应的图。对于一个长度为$n$的排列$dfs$,可以构造一条链$1\rightarrow2\rightarrow3\rightarrow\cdots\rightarrow n$,然后根据$dfs$的顺序依次为链中的每个节点定位其父节点。具体地,在末尾添加$n+1$个空节点$e_1,\ e_2,\ \cdots,\ e_{n+1}$,然后从$dfs[n-1]$开始向前扫描,记录每个节点的父节点直到整条链被还原。如果还原成功,整张图应该是一棵树,即它没有环,所有节点都是相互可达的。

还原完图后,我们可以通过比较给定的DFS序列与还原得到的DFS序列是否相同来检查给定的排列是否是图的有效DFS序列。具体地,我们比较每个对应位置上的节点是否相同,如果有不同的节点,或者有节点在给定的排列中出现的位置与在还原得到的DFS序列中出现的位置不同,则排列不合法。

代码

下面是Python 3的参考实现,其中$adj$数组表示还原得到的图的邻接表,$idx$数组表示每个节点在$dfs$中出现的位置,$dfs$数组表示给定的DFS序列。

from collections import defaultdict

def is_valid_dfs(adj, dfs):
    n = len(adj)
    idx = [-1] * (n + 1)
    for i, node in enumerate(dfs):
        idx[node] = i
    root = dfs[0]
    stack = [root]
    visited = [False] * (n + 1)
    visited[root] = True
    dfs2 = []
    while stack:
        node = stack.pop()
        dfs2.append(node)
        for v in adj[node]:
            if not visited[v]:
                stack.append(v)
                visited[v] = True
    for i in range(n):
        if dfs[i] != dfs2[i] or idx[dfs[i]] != i:
            return False
    return True

示例

考虑如下图:

1 → 2 → 5 → 8
|   | ↙ ↓   ↘
v   v     6 → 9
3   4     | ↘
        v  7
        e_1→e_2→...→e_10

对应的DFS序列是$[1, 2, 5, 8, 6, 9, 7, 4, 3, 10]$。我们可以使用上面的实现来检查其是否是图的有效DFS序列:

adj = defaultdict(list)
adj[1].extend([2, 3])
adj[2].extend([4, 5])
adj[5].append(8)
adj[6].extend([8, 9])
adj[9].append(7)
for i in range(1, 11):
    if i not in adj:
        adj[i] = []
dfs = [1, 2, 5, 8, 6, 9, 7, 4, 3, 10]
print(is_valid_dfs(adj, dfs))  # True