589. N叉树的前序遍历-用写CPU指令执行器的方式写递归遍历

做题目前随便说点

  • 树是一种抽象数据类型,一种具有树结构形式的数据集合。

  • 节点个数确定,有层次关系。

  • 有根节点。

  • 除了根,每个节点有且只有一个父节点。

  • 没有环路。

  • 所有数据结构都可以用链表表示或者用数组表示,树也一样。

589. N叉树的前序遍历

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

例如,给定一个 3叉树 :

1
| \
3 2 4
|
5 6
返回其前序遍历: [1,3,5,6,2,4]。

说明: 递归法很简单,你可以使用迭代法完成此题吗?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题:

审题:

代码框架如下:

class Solution {

    
Node  recursive(Node node) {
        // 边界判断
        if(...) {
            return node;
        }
        
        // 先序遍历node
        
       // 遍历子节点
       for( Node son : node.children ) {
            recursive(son);
        }
        
        // 后序遍历node

        // 返回值。
        return node;
    }
}
  • 多叉树没有绝对意义上的中序遍历。中序遍历是二叉树独有的概念,除非你把n叉树的分支分为两堆?这个看起来有点怪怪的。

开始解题:


/*
// 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 static List<Integer> list;
    Node recursive(Node node) {
        // 边界判断
        if(node == null) {
            return node;
        }
        list.add(node.val);
       // 遍历子节点
       for( Node son : node.children ) {
            recursive(son);
        }
        // 返回值。
        return node;
    }
    public List<Integer> preorder(Node root) {
        list =  new ArrayList();
        recursive(root);
        return list;
    }
}



总结:

迭代法(模拟计算机的递归函数执行过程):

  • 计算机的本质就是一个while循环,然后不停的执行函数指令。
  • 我们可以定义一个函数体,然后将root节点作为函数的参数构造出函数体,交给一个while循环执行。
  • 其实我们知道一个计算机处理器,除了一个while循环,还是一个函数栈stack;
  • 其他与递归有关的细节我们可以耦合到函数体里面去(毕竟我们又不是写虚拟机对吧,不需要做到这个高级的封装。)


/*
// 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 {
    // 定义函数体,等价于一份递归代码
    class FunsBody {
        Node arg;
        public List<Node> otherCallArg;
        int returnVal;
        int programCounter; 
        public FunsBody(Node node) {
            this.arg = node;
            this.otherCallArg = node.children; 
            this.returnVal = node.val;
            this.programCounter = node.children.size();
        }
    }
    // 定数函数输出接口
    class SystemOut {
        public List<Integer> list;
        SystemOut() {
            this.list = new ArrayList();
        }
        public void print(int returnVal) {
            this.list.add(returnVal);
        }
        public List<Integer> getConsole() {
            return this.list;
        }
    }
    public List<Integer> postorder(Node root) {
        SystemOut systemOut =  new SystemOut();
        Stack<FunsBody> funcStack = new Stack();
        // 参数校验
        if( root == null ) {
            return systemOut.getConsole();
        }
        
        // 将代码指令放入函数栈
        funcStack.push(new FunsBody(root));
        
        // 执行函数
        while(!funcStack.isEmpty()) {
            FunsBody func = funcStack.pop();
            if(func.programCounter == 0) {
                // 由于这里我们要模拟后序遍历,所以要等所有指令执行完成,函数才能返回
                systemOut.print(func.returnVal);
            } else {
                // 保护函数现场,将代码指令放入函数栈
                funcStack.push(func);
                // 将新函数的代码指令放入函数栈顶部等待执行
                funcStack.push(new FunsBody(func.otherCallArg.get(func.otherCallArg.size() - func.programCounter)));
                // 调整该函数的PC程序计数器。
                func.programCounter--;
            }
        }
        return systemOut.getConsole();
    }
}

  • 上面我们定义了一个函数体,函数体包含了现场PC程序计数器,以及参数、其他外部函数调用指令和返回值表达式。
  • 还定义了一个函数栈和while循环,while会不停的遍历函数栈中的函数体,如果函数没有外部函数调用(程序计数器将要指向函数体结束位置时),就执行返回值表达式。
  • 如果执行外部函数需要,先保存现场到函数栈,然后将外部函数体入栈顶,调整程序计数器计数器。

代码框架如下:



/*
// 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 {
    // 定义函数体,等价于一份递归代码
    class FunsBody {
        // 参数
        Node arg;
         // 外部函数调用
        public List<Command> otherCall;
        // 返回值表达式
        int returnVal;
        // 程序计数器
        int programCounter; 
        
    }
   
    public List<Integer> postorder(Node root) {
        
        // 边界判断
        if( ....) {
            return systemOut;
        }
        // 开机调用main函数
        
        // 将代码指令放入函数栈,调用main入口函数
        funcStack.push(new FunsBody(root));
        
        // 执行函数
        while(!funcStack.isEmpty()) {
            FunsBody func = funcStack.pop();
            // 程序指针指向完成,返回函数值
            if(func.programCounter == 0) {
                // 由于这里我们要模拟后序遍历,所以要等所有指令执行完成,函数才能返回
                systemOut.print(func.returnVal);
            } else {
                // 否则执行外部函数
                // 保护函数现场,将代码指令放入函数栈
                funcStack.push(func);
                // 将新函数的代码指令放入函数栈顶部等待执行
                funcStack.push(func.nextOutFuncBody);
                // 调整该函数的PC程序计数器。
                func.programCounter--;
            }
        }
        // 释放计算机资源,然后关机
        return ...; 
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章