Нерекурсивный алгоритм первого поиска глубины


Я ищу нерекурсивный алгоритм первого поиска глубины для недвоичного дерева. Любая помощь очень ценится.

15 140

15 ответов:

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() удаляет и возвращает первый элемент в списке.

можно использовать стек, который удерживает узлы, которые еще не были посещены:

stack.push(root)
while !stack.isEmpty() do
    node = stack.pop()
    for each node.childNodes do
        stack.push(stack)
    endfor
    // …
endwhile

Если у вас есть указатели на родительские узлы, вы можете сделать это без дополнительной памяти.

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

используйте стек для отслеживания узлов

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)

}

в то время как "использование стека" может работа в качестве ответа на надуманный вопрос интервью, в действительности, это просто делает явно то, что рекурсивная программа делает за кулисами.

рекурсия использует встроенный стек программ. Когда вы вызываете функцию, она помещает аргументы функции в стек, а когда функция возвращает, она делает это, открывая стек программы.

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);

                }
            }

общая логика заключается в том, чтобы нажать узел (начиная с корня) в стек, Pop () it и Print () value. Затем, если у него есть дети( слева и справа), вставьте их в стек - сначала нажмите вправо, чтобы вы сначала посетили левого ребенка(после посещения самого узла). Когда стек пуст () вы посетили все узлы в предварительном порядке.

реализация ES6 на основе biziclops отличный ответ:

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);
  }
}

Предположим, вы хотите выполнить уведомление при посещении каждого узла в графике. Простая рекурсивная реализация:

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. направленные ребра: с->Т, С->в, т-з, В-З, и V->Т. Запустите собственную реализацию DFS и порядок, в котором узлы должны быть посещены, должен быть: w, t, v, с Неуклюжая реализация DFS, возможно, сначала уведомит t, и это указывает на ошибку. Рекурсивная реализация DFS всегда будет достигать w последним.

нерекурсивный 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 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, вот шаги, чтобы следовать: нажмите первую вершину в стеке, то,

  1. если возможно, посетите соседнюю непросмотренную вершину, отметьте ее, и подтолкнуть его на стек.
  2. если вы не можете следовать шагу 1, то, если это возможно, поп вершину от стек.
  3. если вы не можете следовать шаг 1 или Шаг 2, вы сделали.

вот программа 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:

  • используя только базовые конструкции: переменные, массивы, if, while и for
  • функции 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