概览
我试图找出如何使用某种DFS迭代算法遍历有向循环图。这是我目前实施的一个小版本(它没有处理周期):
class Node(object):
def __init__(self, name):
self.name = name
def start(self):
print '{}_start'.format(self)
def middle(self):
print '{}_middle'.format(self)
def end(self):
print '{}_end'.format(self)
def __str__(self):
return "{0}".format(self.name)
class NodeRepeat(Node):
def __init__(self, name, num_repeats=1):
super(NodeRepeat, self).__init__(name)
self.num_repeats = num_repeats
def dfs(graph, start):
"""Traverse graph from start node using DFS with reversed childs"""
visited = {}
stack = [(start, "")]
while stack:
# To convert dfs -> bfs
# a) rename stack to queue
# b) pop becomes pop(0)
node, parent = stack.pop()
if parent is None:
if visited[node] < 3:
node.end()
visited[node] = 3
elif node not in visited:
if visited.get(parent) == 2:
parent.middle()
elif visited.get(parent) == 1:
visited[parent] = 2
node.start()
visited[node] = 1
stack.append((node, None))
# Maybe you want a different order, if it's so, don't use reversed
childs = reversed(graph.get(node, []))
for child in childs:
if child not in visited:
stack.append((child, node))
if __name__ == "__main__":
Sequence1 = Node('Sequence1')
MtxPushPop1 = Node('MtxPushPop1')
Rotate1 = Node('Rotate1')
Repeat1 = NodeRepeat('Repeat1', num_repeats=2)
Sequence2 = Node('Sequence2')
MtxPushPop2 = Node('MtxPushPop2')
Translate = Node('Translate')
Rotate2 = Node('Rotate2')
Rotate3 = Node('Rotate3')
Scale = Node('Scale')
Repeat2 = NodeRepeat('Repeat2', num_repeats=3)
Mesh = Node('Mesh')
cyclic_graph = {
Sequence1: [MtxPushPop1, Rotate1],
MtxPushPop1: [Sequence2],
Rotate1: [Repeat1],
Sequence2: [MtxPushPop2, Translate],
Repeat1: [Sequence1],
MtxPushPop2: [Rotate2],
Translate: [Rotate3],
Rotate2: [Scale],
Rotate3: [Repeat2],
Scale: [Mesh],
Repeat2: [Sequence2]
}
dfs(cyclic_graph, Sequence1)
print '-'*80
a = Node('a')
b = Node('b')
dfs({
a : [b],
b : [a]
}, a)
上面的代码正在测试几个案例,第一个是下图的某种表示:
第二个是最简单的一个图形包含一个&#34;无限&#34;循环{a->b, b->a}
要求
Repeat
的特殊节点,您可以在其中指示循环循环的迭代次数问题
任何人都可以提供某种知道如何遍历无限/有限循环的解决方案吗?
参考
如果此时此问题尚不清楚,您可以在此article上详细了解此问题,整个想法将使用遍历算法来实现类似于该文章中所示的类似工具。
这里有一个屏幕截图,展示了这种数据结构的全部功能我想弄清楚如何遍历和运行:
答案 0 :(得分:7)
在我开始之前,Run the code on CodeSkulptor!我也希望这些评论能够详细说明我所做的一切。如果您需要更多解释,请查看我对代码下面的递归方法的解释。
# If you don't want global variables, remove the indentation procedures
indent = -1
MAX_THRESHOLD = 10
INF = 1 << 63
def whitespace():
global indent
return '| ' * (indent)
class Node:
def __init__(self, name, num_repeats=INF):
self.name = name
self.num_repeats = num_repeats
def start(self):
global indent
if self.name.find('Sequence') != -1:
print whitespace()
indent += 1
print whitespace() + '%s_start' % self.name
def middle(self):
print whitespace() + '%s_middle' % self.name
def end(self):
global indent
print whitespace() + '%s_end' % self.name
if self.name.find('Sequence') != -1:
indent -= 1
print whitespace()
def dfs(graph, start):
visits = {}
frontier = [] # The stack that keeps track of nodes to visit
# Whenever we "visit" a node, increase its visit count
frontier.append((start, start.num_repeats))
visits[start] = visits.get(start, 0) + 1
while frontier:
# parent_repeat_count usually contains vertex.repeat_count
# But, it may contain a higher value if a repeat node is its ancestor
vertex, parent_repeat_count = frontier.pop()
# Special case which signifies the end
if parent_repeat_count == -1:
vertex.end()
# We're done with this vertex, clear visits so that
# if any other node calls us, we're still able to be called
visits[vertex] = 0
continue
# Special case which signifies the middle
if parent_repeat_count == -2:
vertex.middle()
continue
# Send the start message
vertex.start()
# Add the node's end state to the stack first
# So that it is executed last
frontier.append((vertex, -1))
# No more children, continue
# Because of the above line, the end method will
# still be executed
if vertex not in graph:
continue
## Uncomment the following line if you want to go left to right neighbor
#### graph[vertex].reverse()
for i, neighbor in enumerate(graph[vertex]):
# The repeat count should propagate amongst neighbors
# That is if the parent had a higher repeat count, use that instead
repeat_count = max(1, parent_repeat_count)
if neighbor.num_repeats != INF:
repeat_count = neighbor.num_repeats
# We've gone through at least one neighbor node
# Append this vertex's middle state to the stack
if i >= 1:
frontier.append((vertex, -2))
# If we've not visited the neighbor more times than we have to, visit it
if visits.get(neighbor, 0) < MAX_THRESHOLD and visits.get(neighbor, 0) < repeat_count:
frontier.append((neighbor, repeat_count))
visits[neighbor] = visits.get(neighbor, 0) + 1
def dfs_rec(graph, node, parent_repeat_count=INF, visits={}):
visits[node] = visits.get(node, 0) + 1
node.start()
if node not in graph:
node.end()
return
for i, neighbor in enumerate(graph[node][::-1]):
repeat_count = max(1, parent_repeat_count)
if neighbor.num_repeats != INF:
repeat_count = neighbor.num_repeats
if i >= 1:
node.middle()
if visits.get(neighbor, 0) < MAX_THRESHOLD and visits.get(neighbor, 0) < repeat_count:
dfs_rec(graph, neighbor, repeat_count, visits)
node.end()
visits[node] = 0
Sequence1 = Node('Sequence1')
MtxPushPop1 = Node('MtxPushPop1')
Rotate1 = Node('Rotate1')
Repeat1 = Node('Repeat1', 2)
Sequence2 = Node('Sequence2')
MtxPushPop2 = Node('MtxPushPop2')
Translate = Node('Translate')
Rotate2 = Node('Rotate2')
Rotate3 = Node('Rotate3')
Scale = Node('Scale')
Repeat2 = Node('Repeat2', 3)
Mesh = Node('Mesh')
cyclic_graph = {
Sequence1: [MtxPushPop1, Rotate1],
MtxPushPop1: [Sequence2],
Rotate1: [Repeat1],
Sequence2: [MtxPushPop2, Translate],
Repeat1: [Sequence1],
MtxPushPop2: [Rotate2],
Translate: [Rotate3],
Rotate2: [Scale],
Rotate3: [Repeat2],
Scale: [Mesh],
Repeat2: [Sequence2]
}
dfs(cyclic_graph, Sequence1)
print '-'*40
dfs_rec(cyclic_graph, Sequence1)
print '-'*40
dfs({Sequence1: [Translate], Translate: [Sequence1]}, Sequence1)
print '-'*40
dfs_rec({Sequence1: [Translate], Translate: [Sequence1]}, Sequence1)
可以找到输入和(格式良好且缩进)输出here。如果您想查看 我是如何格式化输出的,请参阅代码,也可以是found on CodeSkulptor。
对,解释。更容易理解但更低效的递归解决方案,我将用来帮助解释,如下:
def dfs_rec(graph, node, parent_repeat_count=INF, visits={}):
visits[node] = visits.get(node, 0) + 1
node.start()
if node not in graph:
node.end()
return
for i, neighbor in enumerate(graph[node][::-1]):
repeat_count = max(1, parent_repeat_count)
if neighbor.num_repeats != INF:
repeat_count = neighbor.num_repeats
if i >= 1:
node.middle()
if visits.get(neighbor, 0) < MAX_THRESHOLD and visits.get(neighbor, 0) < repeat_count:
dfs_rec(graph, neighbor, repeat_count, visits)
node.end()
visits[node] = 0
start
事件。end
事件并返回。graph[node][::-1]
)来维持邻居遍历的相同顺序(从右到左),如在迭代版本中那样。
middle
事件(不邻居)。MAX_THRESHOLD
次(对于伪无限循环)和b)上述计算的重复计数次数来完成的。end
事件并清除其在哈希表中的访问。这样做是为了在其他节点再次调用它时,它不会使可访问性检查失败和/或执行的次数少于所需的次数。答案 1 :(得分:1)
根据comment66244567 - 通过忽略到访问节点的链接并执行广度优先搜索将图形缩减为树,因为这会产生更自然(并且可能更平衡)的树:
protected void Button1_Click(object sender, EventArgs e)
{
GridView1.DataSource = null;
GridView1.DataBind();
connect = new SqlConnection(@"Data Source=LP12;Initial Catalog=Data;Integrated Security=True");
connect.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = connect;
cmd.CommandText = "SELECT DrukSensor, FlowSensor
+ " FROM SysteemSensorInfo";
DataSet ds = new DataSet();
new SqlDataAdapter(cmd).Fill(ds);
GridView1.DataSource = ds.Tables[0];
GridView1.DataBind();
GridView1.AutoGenerateColumns = false;
}
使用您的图表和def traverse(graph,node,process):
seen={node}
current_level=[node]
while current_level:
next_level=[]
for node in current_level:
process(node)
for child in (link for link in graph.get(node,[]) if link not in seen):
next_level.append(child)
seen.add(child)
current_level=next_level
,会产生:
def process(node): print node
另一个BFS算法iterative deepening DFS(以速度为代价使用更少的内存)在这种情况下不会赢得任何东西:因为你必须存储对被访问节点的引用,你已经消耗了 O(n)记忆。你不需要产生中间结果(但无论如何你都可以 - 例如In [24]: traverse(cyclic_graph,Sequence1,process)
Sequence1
MtxPushPop1
Rotate1
Sequence2
Repeat1
MtxPushPop2
Translate
Rotate2
Rotate3
Scale
Repeat2
Mesh
处理一个级别后的东西。)