树的四种遍历方式(递归与非递归实现c++&java)

目录

树的遍历:

1.前序遍历

2.中序遍历

3.后序遍历

4.层序遍历


1.前序遍历

        前序遍历即DLR,先访问根节点再访问左子树,再访问右子树,因此需要遍历和回溯

递归:递归遍历左子树,遍历的同时,用list保存节点的值用s保存遍历过的节点,走到底之后就回溯,访问上一个节点的有节点,重复以上过程,直到访问到最后一个节点(左右都为空)

java:

class Solution {
    List<Integer> list=new ArrayList<Integer>();
    public List<Integer> preorderTraversal(TreeNode root) {
        if(root==null) return list;
        list.add(root.val);
        preorderTraversal(root.left);
        preorderTraversal(root.right);
        return list;
    }
}

c++:

class Solution {
public:
    vector<int> res;
    vector<int> preorderTraversal(TreeNode* root) {
        if(root==NULL) return res;
        res.push_back(root->val);
        preorderTraversal(root->left);
        preorderTraversal(root->right);
        return res;
    }
};

非递归:其实在递归的过程中隐含着一个栈,用非递归的方法来实现的时候,我们就要把栈显示的表示出来,先访问根节点,因此在遍历的时候,用list来保存根节点的值,用一个栈来保存根节点,一直到访问到最左下的一个节点(即此时node=null),再从栈中取出节点,对每一个节点继续遍历重复这个过程,一直到栈和节点的左右孩子都空

java:

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list=new ArrayList<Integer>();
        if(root==null) return list;
        
        Deque<TreeNode> s=new LinkedList<TreeNode>();
        TreeNode node=root;
        while(!s.isEmpty()||node!=null){
            while(node!=null){
                list.add(node.val);
                s.push(node);
                node=node.left;
            }
            node=s.pop();
            node=node.right;
        }
        return list;
    }
}

c++: 

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        if(root==NULL) return res;
        stack<TreeNode *> s;
        TreeNode* node=root;
        while(node!=NULL||!s.empty()){
            while(node!=NULL){
                res.push_back(node->val);
                s.push(node);
                node=node->left;
            }
            node=s.top();
            s.pop();
            node=node->right;
        }
        return res;
    }
};

2.中序遍历

        中序遍历就是LDR,先访问左子树,再访问根节点最后访问右子树,也需要遍历和回溯

递归:从根节点开始,遍历左子树到最左侧的节点并保存节点的值,与前序遍历不同的是,中序遍历一直到最左下的节点再开始保存节点的值,然后回溯

java:

class Solution {
    List<Integer> list=new ArrayList();
    public List<Integer> inorderTraversal(TreeNode root) {
        if(root==null) return list;
        inorderTraversal(root.left);
        list.add(root.val);
        inorderTraversal(root.right);
        return list;
    }
}

c++: 

class Solution {
public:
    vector<int> res;
    vector<int> inorderTraversal(TreeNode* root) {
        
        if(root==NULL) return res;
        inorderTraversal(root->left);
        res.push_back(root->val);
        inorderTraversal(root->right);
        return res;
    }
};

非递归:同前序遍历,中序遍历递归的过程也是隐含了一个栈的调用,我们在迭代的时候,将栈显示的表示出来,从根节点开始遍历,一直到最左下的节点,遍历的过程用s保存该节点的值,每次遍历结束就用list保存该节点的值,并访问该节点的右节点重复上述过程,直到栈和节点的左右孩子都空

java:

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list=new ArrayList();
        if(root==null) return list;
        Deque<TreeNode> s=new LinkedList();
        TreeNode node=root;
        while(node!=null||!s.isEmpty()){
            while(node!=null){
                s.push(node);
                node=node.left;
            }
            node=s.pop();
            list.add(node.val);
            node=node.right;
        }
        return list;
    }
}

c++: 

class Solution {
vector<int> inorderTraversal(TreeNode* root) {
        stack<TreeNode*> s;
        vector<int> res;
        while(!s.empty()||root!=NULL){
            while(root!=NULL){
                s.push(root);
                root=root->left;
            }
            TreeNode *pr=s.top();
            s.pop();
            res.push_back(pr->val);
            root=pr->right;
        }
        return res;
    }
};

3.后序遍历

        后序遍历即LRD,先访问左子树,再访问右子树,最后访问根节点,因此也需要遍历和回溯

递归:从根节点开始,递归访问左子树,再递归访问右子树,最后访问根节点,因此,每一轮的递归结束后再保存节点的值

java:

class Solution {
    List<Integer> list=new ArrayList();
    public List<Integer> postorderTraversal(TreeNode root) {
        if(root==null) return list;
        postorderTraversal(root.left);
        postorderTraversal(root.right);
        list.add(root.val);
        return list;
    }
}

c++:

class Solution {
public:
    vector<int> res;
    vector<int> postorderTraversal(TreeNode* root) {
        if(root==NULL) return res;
        postorderTraversal(root->left);
        postorderTraversal(root->right);
        res.push_back(root->val);
        return res;
    }
};

非递归:后序遍历不能像前序和中序遍历一样来只用栈回溯了,因为我们在回溯的过程,并不知道上一个节点是左节点还是右节点,只有访问过右节点我们才能访问根节点,如果是左节点,我们就要先访问右节点再访问根节点,因此,我们需要额外添加一个变量来保存上一个访问的节点(prenode),如果正在访问的节点(curnode)的右节点是prenode或者为null,说明右节点已被访问或者没有右节点,那就可以直接保存curnode,但如果不是,那就需要先访问右节点,每次访问结束都应该修改prenode为curnode

 java:

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list=new ArrayList();
        if(root==null) return list;
        Deque<TreeNode> s=new LinkedList();
        TreeNode preNode=null;
        TreeNode curNode=root;
        while(curNode!=null){
            s.push(curNode);
            curNode=curNode.left;
        }
        while(!s.isEmpty()){
            curNode=s.pop();
            if(curNode.right==null||curNode.right==preNode){
                list.add(curNode.val);
                preNode=curNode;
            }
            else{
                s.push(curNode);
                curNode=curNode.right;
                while(curNode!=null){
                    s.push(curNode);
                    curNode=curNode.left;
                }
            }
        }
        return list;
    }
}

c++:

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        TreeNode *preNode=NULL;
        TreeNode *curNode=root;
        vector<int> res;
        if(root==NULL) return res;
        stack<TreeNode*> s;
        while(curNode!=NULL){
            s.push(curNode);
            curNode=curNode->left;
        }
        while(!s.empty()){
            curNode=s.top();
            s.pop();
            if(curNode->right==NULL||curNode->right==preNode){
                res.push_back(curNode->val);
                preNode=curNode;
            }
            else{
                s.push(curNode);
                curNode=curNode->right;
                while(curNode){
                    s.push(curNode);
                    curNode=curNode->left;
                }
            }
        }
        return res;
    }
};

4.层序遍历

        层序遍历即一层层的访问,那就不需要回溯,但需要有一个队列来保存当前节点值,这样就可以通过该节点去访问它的孩子节点,直到访问到叶子节点结束

java:

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list=new ArrayList();
        Queue<TreeNode> q=new LinkedList();
        if(root==null) return list;
        q.add(root);
        while(!q.isEmpty()){
            TreeNode p=q.poll();
            list.add(p.val);
            if(p.left!=null){
                q.add(p.left);
            }
            if(p.right!=null){
                q.add(p.right);
            }
        }
        return list;
    }
}

c++:

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        queue<TreeNode*> q;
        vector<int> res;
        if(root==NULL) return res;
        q.push(root);
        while(!q.empty()){
            TreeNode *p=q.front();
            q.pop();
            res.push_back(p->val);
            if(p->left!=NULL) q.push(p->left);
            if(p->right!=NULL) q.push(p->right);
        }
        return res;
    }
};

其实非递归可以背一个模板来用 我真聪明

while(!s.empty()||root!=NULL){
            while(root!=NULL){
                s.push(root);
                root=root->left;
                //前序在这里加上res.push_back(pr->val);
            }
            TreeNode *pr=s.top();
            s.pop();
            //中序在这里加上res.push_back(pr->val);
            root=pr->right;
            //后序要在这里判断右节点再做相应操作
        }
        return res;

终于写完啦耶,希望能够帮到大家,欢迎留言和提问,如有错误,欢迎指正,创作不易,谢谢支持

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值