Я шукаю алгоритм пошуку першого нерекурсивного глибини для небінарного дерева. Будь-яка допомога дуже цінується.
Я шукаю алгоритм пошуку першого нерекурсивного глибини для небінарного дерева. Будь-яка допомога дуже цінується.
Відповіді:
DFS:
list nodes_to_visit = {root};
while( nodes_to_visit isn't empty ) {
currentnode = nodes_to_visit.take_first();
nodes_to_visit.prepend( currentnode.children );
//do something
}
BFS:
list nodes_to_visit = {root};
while( nodes_to_visit isn't empty ) {
currentnode = nodes_to_visit.take_first();
nodes_to_visit.append( currentnode.children );
//do something
}
Симетрія цих двох досить класна.
Оновлення: як зазначалося, take_first()
вилучає та повертає перший елемент у списку.
.first()
функція також видаляє елемент зі списку. Як і shift()
в багатьох мовах. pop()
також працює і повертає дочірні вузли в порядку справа наліво замість зліва направо.
gray(1st)->gray(2nd)->gray(3rd)->blacken(3rd)->blacken(2nd)->blacken(1st)
. Але ваш код виробляє: gray(1st)->gray(2nd)->gray(3rd)->blacken(2nd)->blacken(3rd)->blacken(1st)
.
Ви б використовували стек, який містить вузли, які ще не були відвідані:
stack.push(root)
while !stack.isEmpty() do
node = stack.pop()
for each node.childNodes do
stack.push(stack)
endfor
// …
endwhile
if (nodes are not marked)
щоб судити, чи підходить його натиснення до стека. Це може спрацювати?
doing cycles
? Я думаю, що я просто хочу розпорядження ДФС. Це правильно чи ні, дякую.
for each node.childNodes.reverse() do stack.push(stack) endfor
). Це, мабуть, і те, що ви хочете. Приємне пояснення, чому це так, є у цьому відео: youtube.com/watch?v=cZPXfl_tUkA endfor
Якщо у вас є вказівники на батьківські вузли, ви можете це зробити без додаткової пам'яті.
def dfs(root):
node = root
while True:
visit(node)
if node.first_child:
node = node.first_child # walk down
else:
while not node.next_sibling:
if node is root:
return
node = node.parent # walk up ...
node = node.next_sibling # ... and right
Зауважте, що якщо дочірні вузли зберігаються як масив, а не через покажчики братів, наступний брат може бути знайдений як:
def next_sibling(node):
try:
i = node.parent.child_nodes.index(node)
return node.parent.child_nodes[i+1]
except (IndexError, AttributeError):
return None
while not node.next_sibling or node is root:
.
Використовуйте стек для відстеження своїх вузлів
Stack<Node> s;
s.prepend(tree.head);
while(!s.empty) {
Node n = s.poll_front // gets first node
// do something with q?
for each child of n: s.prepend(child)
}
Хоча "використовувати стек" може бути працювати як відповідь на надумане запитання про інтерв'ю, насправді це просто явно робить те, що робить рекурсивна програма за кадром.
Рекурсія використовує вбудований стек програм. Коли ви викликаєте функцію, вона висуває аргументи функції на стек, а коли функція повертається, це робить, вискакуючи стек програми.
Реалізація ES6, заснована на відмінній відповіді:
root = {
text: "root",
children: [{
text: "c1",
children: [{
text: "c11"
}, {
text: "c12"
}]
}, {
text: "c2",
children: [{
text: "c21"
}, {
text: "c22"
}]
}, ]
}
console.log("DFS:")
DFS(root, node => node.children, node => console.log(node.text));
console.log("BFS:")
BFS(root, node => node.children, node => console.log(node.text));
function BFS(root, getChildren, visit) {
let nodesToVisit = [root];
while (nodesToVisit.length > 0) {
const currentNode = nodesToVisit.shift();
nodesToVisit = [
...nodesToVisit,
...(getChildren(currentNode) || []),
];
visit(currentNode);
}
}
function DFS(root, getChildren, visit) {
let nodesToVisit = [root];
while (nodesToVisit.length > 0) {
const currentNode = nodesToVisit.shift();
nodesToVisit = [
...(getChildren(currentNode) || []),
...nodesToVisit,
];
visit(currentNode);
}
}
PreOrderTraversal is same as DFS in binary tree. You can do the same recursion
taking care of Stack as below.
public void IterativePreOrder(Tree root)
{
if (root == null)
return;
Stack s<Tree> = new Stack<Tree>();
s.Push(root);
while (s.Count != 0)
{
Tree b = s.Pop();
Console.Write(b.Data + " ");
if (b.Right != null)
s.Push(b.Right);
if (b.Left != null)
s.Push(b.Left);
}
}
Загальна логіка полягає в тому, щоб натиснути вузол (починаючи з кореня) у значення Stack, Pop () it та Print (). Потім, якщо у нього є діти (ліворуч і праворуч), натисніть їх у групу - спочатку натисніть праворуч, щоб ви спочатку відвідали Ліву дитину (після відвідування самого вузла). Коли стек порожній (), ви відвідали всі вузли перед попереднім замовленням.
Нерекурсивний DFS з використанням генераторів ES6
class Node {
constructor(name, childNodes) {
this.name = name;
this.childNodes = childNodes;
this.visited = false;
}
}
function *dfs(s) {
let stack = [];
stack.push(s);
stackLoop: while (stack.length) {
let u = stack[stack.length - 1]; // peek
if (!u.visited) {
u.visited = true; // grey - visited
yield u;
}
for (let v of u.childNodes) {
if (!v.visited) {
stack.push(v);
continue stackLoop;
}
}
stack.pop(); // black - all reachable descendants were processed
}
}
Він відхиляється від типового нерекурсивного DFS, щоб легко виявити, коли всі доступні нащадки даного вузла були оброблені та підтримувати поточний шлях у списку / стеку.
Припустимо, ви хочете виконати сповіщення під час відвідування кожного вузла в графі. Проста рекурсивна реалізація:
void DFSRecursive(Node n, Set<Node> visited) {
visited.add(n);
for (Node x : neighbors_of(n)) { // iterate over all neighbors
if (!visited.contains(x)) {
DFSRecursive(x, visited);
}
}
OnVisit(n); // callback to say node is finally visited, after all its non-visited neighbors
}
Гаразд, тепер ви хочете виконати на основі стека, тому що ваш приклад не працює. Наприклад, складні графіки можуть призвести до того, що це призведе до розбиття стека вашої програми, і вам потрібно реалізувати нерекурсивну версію. Найбільше питання - знати, коли опублікувати повідомлення.
Наступний псевдо-код працює (поєднання Java та C ++ для читабельності):
void DFS(Node root) {
Set<Node> visited;
Set<Node> toNotify; // nodes we want to notify
Stack<Node> stack;
stack.add(root);
toNotify.add(root); // we won't pop nodes from this until DFS is done
while (!stack.empty()) {
Node current = stack.pop();
visited.add(current);
for (Node x : neighbors_of(current)) {
if (!visited.contains(x)) {
stack.add(x);
toNotify.add(x);
}
}
}
// Now issue notifications. toNotifyStack might contain duplicates (will never
// happen in a tree but easily happens in a graph)
Set<Node> notified;
while (!toNotify.empty()) {
Node n = toNotify.pop();
if (!toNotify.contains(n)) {
OnVisit(n); // issue callback
toNotify.add(n);
}
}
Це виглядає складно, але додаткова логіка, необхідна для видачі сповіщень, існує тому, що потрібно повідомляти в зворотному порядку відвідування - DFS запускається в корені, але повідомляє про це останнім часом, на відміну від BFS, який дуже простий у виконанні.
Для ударів спробуйте наступний графік: вузли s, t, v і w. спрямовані ребра: s-> t, s-> v, t-> w, v-> w і v-> t. Запустіть власну реалізацію DFS і порядок відвідування вузлів повинен бути: w, t, v, s Незграбна реалізація DFS, можливо, спочатку сповістить t, а це вказує на помилку. Рекурсивна реалізація DFS завжди досягала останнього.
ПОВНИЙ приклад РОБОЧИЙ код, без стека:
import java.util.*;
class Graph {
private List<List<Integer>> adj;
Graph(int numOfVertices) {
this.adj = new ArrayList<>();
for (int i = 0; i < numOfVertices; ++i)
adj.add(i, new ArrayList<>());
}
void addEdge(int v, int w) {
adj.get(v).add(w); // Add w to v's list.
}
void DFS(int v) {
int nodesToVisitIndex = 0;
List<Integer> nodesToVisit = new ArrayList<>();
nodesToVisit.add(v);
while (nodesToVisitIndex < nodesToVisit.size()) {
Integer nextChild= nodesToVisit.get(nodesToVisitIndex++);// get the node and mark it as visited node by inc the index over the element.
for (Integer s : adj.get(nextChild)) {
if (!nodesToVisit.contains(s)) {
nodesToVisit.add(nodesToVisitIndex, s);// add the node to the HEAD of the unvisited nodes list.
}
}
System.out.println(nextChild);
}
}
void BFS(int v) {
int nodesToVisitIndex = 0;
List<Integer> nodesToVisit = new ArrayList<>();
nodesToVisit.add(v);
while (nodesToVisitIndex < nodesToVisit.size()) {
Integer nextChild= nodesToVisit.get(nodesToVisitIndex++);// get the node and mark it as visited node by inc the index over the element.
for (Integer s : adj.get(nextChild)) {
if (!nodesToVisit.contains(s)) {
nodesToVisit.add(s);// add the node to the END of the unvisited node list.
}
}
System.out.println(nextChild);
}
}
public static void main(String args[]) {
Graph g = new Graph(5);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 2);
g.addEdge(2, 0);
g.addEdge(2, 3);
g.addEdge(3, 3);
g.addEdge(3, 1);
g.addEdge(3, 4);
System.out.println("Breadth First Traversal- starting from vertex 2:");
g.BFS(2);
System.out.println("Depth First Traversal- starting from vertex 2:");
g.DFS(2);
}}
вихід: Перша обробка ширини - починаючи з вершини 2: 2 0 3 1 4 Глибина Перша обробка - починаючи з вершини 2: 2 3 4 1 0
Можна використовувати стек. Я реалізував графіки за допомогою матриці суміжності:
void DFS(int current){
for(int i=1; i<N; i++) visit_table[i]=false;
myStack.push(current);
cout << current << " ";
while(!myStack.empty()){
current = myStack.top();
for(int i=0; i<N; i++){
if(AdjMatrix[current][i] == 1){
if(visit_table[i] == false){
myStack.push(i);
visit_table[i] = true;
cout << i << " ";
}
break;
}
else if(!myStack.empty())
myStack.pop();
}
}
}
Ітеративний DFS на Java:
//DFS: Iterative
private Boolean DFSIterative(Node root, int target) {
if (root == null)
return false;
Stack<Node> _stack = new Stack<Node>();
_stack.push(root);
while (_stack.size() > 0) {
Node temp = _stack.peek();
if (temp.data == target)
return true;
if (temp.left != null)
_stack.push(temp.left);
else if (temp.right != null)
_stack.push(temp.right);
else
_stack.pop();
}
return false;
}
http://www.youtube.com/watch?v=zLZhSSXAwxI
Просто подивився це відео і вийшов з реалізацією. Мені це легко зрозуміти. Будь ласка, критикуйте це.
visited_node={root}
stack.push(root)
while(!stack.empty){
unvisited_node = get_unvisited_adj_nodes(stack.top());
If (unvisited_node!=null){
stack.push(unvisited_node);
visited_node+=unvisited_node;
}
else
stack.pop()
}
Використовуючи Stack
, ось наступні кроки: Натисніть першу вершину на стек, потім,
Ось програма Java, виконана вищезазначеними кроками:
public void searchDepthFirst() {
// begin at vertex 0
vertexList[0].wasVisited = true;
displayVertex(0);
stack.push(0);
while (!stack.isEmpty()) {
int adjacentVertex = getAdjacentUnvisitedVertex(stack.peek());
// if no such vertex
if (adjacentVertex == -1) {
stack.pop();
} else {
vertexList[adjacentVertex].wasVisited = true;
// Do something
stack.push(adjacentVertex);
}
}
// stack is empty, so we're done, reset flags
for (int j = 0; j < nVerts; j++)
vertexList[j].wasVisited = false;
}
Stack<Node> stack = new Stack<>();
stack.add(root);
while (!stack.isEmpty()) {
Node node = stack.pop();
System.out.print(node.getData() + " ");
Node right = node.getRight();
if (right != null) {
stack.push(right);
}
Node left = node.getLeft();
if (left != null) {
stack.push(left);
}
}
Псевдокод на основі відповіді @ biziclop:
getNode(id)
таgetChildren(id)
N
ПРИМІТКА. Я використовую індексацію масиву від 1, а не до 0.
Ширина перша
S = Array(N)
S[1] = 1; // root id
cur = 1;
last = 1
while cur <= last
id = S[cur]
node = getNode(id)
children = getChildren(id)
n = length(children)
for i = 1..n
S[ last+i ] = children[i]
end
last = last+n
cur = cur+1
visit(node)
end
Глибина перша
S = Array(N)
S[1] = 1; // root id
cur = 1;
while cur > 0
id = S[cur]
node = getNode(id)
children = getChildren(id)
n = length(children)
for i = 1..n
// assuming children are given left-to-right
S[ cur+i-1 ] = children[ n-i+1 ]
// otherwise
// S[ cur+i-1 ] = children[i]
end
cur = cur+n-1
visit(node)
end
Ось посилання на java-програму, що показує DFS, що дотримується як рекурсивних, так і нерекурсивних методів, а також обчислює час відкриття та закінчення , але немає межі розміщення.
public void DFSIterative() {
Reset();
Stack<Vertex> s = new Stack<>();
for (Vertex v : vertices.values()) {
if (!v.visited) {
v.d = ++time;
v.visited = true;
s.push(v);
while (!s.isEmpty()) {
Vertex u = s.peek();
s.pop();
boolean bFinished = true;
for (Vertex w : u.adj) {
if (!w.visited) {
w.visited = true;
w.d = ++time;
w.p = u;
s.push(w);
bFinished = false;
break;
}
}
if (bFinished) {
u.f = ++time;
if (u.p != null)
s.push(u.p);
}
}
}
}
}
Повне джерело тут .
Просто хотів додати мою реалізацію python до довгого списку рішень. Цей нерекурсивний алгоритм має відкриття та завершені події.
worklist = [root_node]
visited = set()
while worklist:
node = worklist[-1]
if node in visited:
# Node is finished
worklist.pop()
else:
# Node is discovered
visited.add(node)
for child in node.children:
worklist.append(child)