攻不下dfs不参加比赛(二十)

487 阅读4分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

为什么练dfs

相信学过数据结构的朋友都知道dfs(深度优先搜索)是里面相当重要的一种搜索算法,可能直接说大家感受不到有条件的大家可以去看看一些算法比赛。这些比赛中每一届或多或少都会牵扯到dfs,可能提到dfs大家都知道但是我们为了避免眼高手低有的东西看着自己很明白就是写不出来。为了避免这种尴尬我们这几天乘着这个活动练练,好了我们话不多说开始肥学。

PS:这两天发现有的肥友不知道什么是DFS我还是简单说一下吧不然这题很难做下去。

深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次.

在这里插入图片描述

举例说明之:下图是一个无向图,如果我们从A点发起深度优先搜索(以下的访问次序并不是唯一的,第二个点既可以是B也可以是C,D),则我们可能得到如下的一个访问过程:A->B->E(没有路了!回溯到A)->C->F->H->G->D(没有路,最终回溯到A,A也没有未访问的相邻节点,本次搜索结束).简要说明深度优先搜索的特点:每次深度优先搜索的结果必然是图的一个连通分量.深度优先搜索可以从多点发起.如果将每个节点在深度优先搜索过程中的"结束时间"排序(具体做法是创建一个list,然后在每个节点的相邻节点都已被访问的情况下,将该节点加入list结尾,然后逆转整个链表),则我们可以得到所谓的"拓扑排序",即topological sort. [1]

题目

注意:这道题还不会的同学请务必把我前面的文章看看,因为本次的题目较为简单而且多次练习过了。

给定一个 n 叉树的根节点 root ,返回 其节点值的 前序遍历 。

n 叉树 在输入中按层序遍历进行序列化表示,每组子节点由空值 null 分隔(请参见示例)。

示例 1:

在这里插入图片描述

输入:root = [1,null,3,2,4,null,5,6]
输出:[1,3,5,6,2,4]

示例 2: 在这里插入图片描述

输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
输出:[1,2,3,6,7,11,14,4,8,12,5,9,13,10]
 

思路:和二叉树的前序遍历基本思路一致大家可以自行摸索这次解的一和二都是一样的思路只不过二更简洁。

解一:dfs

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
    public List<Integer> feixue(Node root,List<Integer> list){
         if(root==null)return list;
        List<Node> listchil=root.children;
        for(Node n:listchil){
            feixue(n,list);
            list.add(n.val);
        }
        return list;
    }
    public List<Integer> postorder(Node root) {
        List<Integer> list=new ArrayList<Integer>();
        if(root==null)return list;
        feixue(root,list);
        list.add(root.val);
        return list;
    }
}

解二:迭代 思路:

在这里的栈模拟中比较难处理的在于从当前节点 uu 的子节点 v_1v 1 ​ 返回时,此时需要处理节点 uu 的下一个节点 v_2v 2 ​ ,此时需要记录当前已经遍历完成哪些子节点,才能找到下一个需要遍历的节点。在二叉树树中因为只有左右两个子节点,因此比较方便处理,在 NN 叉树中由于有多个子节点,因此使用哈希表记录当前节点 uu 已经访问过哪些子节点。

每次入栈时都将当前节点的 uu 的第一个子节点压入栈中,直到当前节点为空节点为止。 每次查看栈顶元素 pp,如果节点 pp 的子节点已经全部访问过,则将节点 pp 的从栈中弹出,并从哈希表中移除,表示该以该节点的子树已经全部遍历过;如果当前节点 pp 的子节点还有未遍历的,则将当前节点的 pp 的下一个未访问的节点压入到栈中,重复上述的入栈操作。

class Solution {
    public List<Integer> preorder(Node root) {
        List<Integer> res = new ArrayList<Integer>();
        if (root == null) {
            return res;
        }
        Map<Node, Integer> map = new HashMap<Node, Integer>();
        Deque<Node> stack = new ArrayDeque<Node>();
        Node node = root;
        while (!stack.isEmpty() || node != null) {
            while (node != null) {
                res.add(node.val);
                stack.push(node);
                List<Node> children = node.children;
                if (children != null && children.size() > 0) {
                    map.put(node, 0);
                    node = children.get(0);
                } else {
                    node = null;
                }
            }
            node = stack.peek();
            int index = map.getOrDefault(node, -1) + 1;
            List<Node> children = node.children;
            if (children != null && children.size() > index) {
                map.put(node, index);
                node = children.get(index);
            } else {
                stack.pop();
                map.remove(node);
                node = null;
            }
        }
        return res;
    }
}