算法学习——栈和深度优先搜索

1,143 阅读2分钟

这是我参与更文挑战的第24天,活动详情查看:更文挑战

栈:先入后出的数据结构

在LIFO数据结构中,将首先处理添加到队列中的最新元素。与队列不同,栈是一个 LIFO 数据结构。通常,插入操作在栈中被称作入栈 push 。与队列类似,总是在堆栈的末尾添加一个新元素。但是,删除操作,退栈 pop ,将始终删除队列中相对于它的最后一个元素。

栈的简单实现

class MyStack {
    private List<Integer> data;//存储元素
    public MyStack() {
        data = new ArrayList<>();
    }
    /** 入栈. */
    public void push(int x) {
        data.add(x);
    }
    /** 判空 */
    public boolean isEmpty() {
        return data.isEmpty();
    }
    /** 获取栈顶元素 */
    public int top() {
        return data.get(data.size() - 1);
    }
    /** 出栈 */
    public boolean pop() {
        if (isEmpty()) {
            return false;
        }
        data.remove(data.size() - 1);
        return true;
    }
};

public class Main {
    public static void main(String[] args) {
        MyStack s = new MyStack();
        s.push(1);
        s.push(2);
        s.push(3);
        for (int i = 0; i < 4; ++i) {
            if (!s.isEmpty()) {
                System.out.println(s.top());
            }
            System.out.println(s.pop());
        }
    }
}

栈和深度优先搜索

与 BFS 类似,深度优先搜索(DFS)是用于在树/图中遍历/搜索的另一种重要算法。也可以在更抽象的场景中使用。

DFS中有三种遍历方式:先序遍历、中序遍历和后序遍历,三种遍历方式有一个共同的特性:除非到达最深的节点,否则不会回溯。

先序遍历

前序遍历首先访问根节点,然后遍历左子树,最后遍历右子树。

先序遍历.png

中序遍历

中序遍历是先遍历左子树,然后访问根节点,然后遍历右子树。

中序遍历.png

后序遍历

后序遍历是先遍历左子树,然后遍历右子树,最后访问树的根节点。

后序遍历.png

值得注意的是,当删除树中的节点时,删除过程将按照后序遍历的顺序进行。 也就是说,当你删除一个节点时,你将首先删除它的左节点和它的右边的节点,然后再删除节点本身。

递归方式深度优先搜索

当使用递归方式实现DFS时,似乎没有使用任何栈,实际上使用的系统提供的函数调用栈。

/*
 * Return true if there is a path from cur to target.
 */
boolean DFS(Node cur, Node target, Set<Node> visited) {
    return true if cur is target;
    for (next : each neighbor of cur) {
        if (next is not in visited) {
            add next to visted;
            return true if DFS(next, target, visited) == true;
        }
    }
    return false;
}

显示使用栈结构实现深度优先搜索

递归方案的优点是更容易实现。但是,存在一个很大的缺点:当递归的深度太深时,容易遭受堆栈溢出。使用显示栈实现深度优先,可以规避这个问题。

/*
 * Return true if there is a path from cur to target.
 */
boolean DFS(int root, int target) {
    Set<Node> visited;
    Stack<Node> s;
    add root to s;
    while (s is not empty) {
        Node cur = the top element in s;
        return true if cur is target;
        for (Node next : the neighbors of cur) {
            if (next is not in visited) {
                add next to s;
                add next to visited;
            }
        }
        remove cur from s;
    }
    return false;
}