java 递归广度优先拼接树结构 递归广度优先遍历_入栈


如上图二叉树的遍历主要思想就是通过递归方式进行遍历,同时如果要非递归遍历的话,一般情况下,深度优先遍历需要借助stack保存中间变量的方式进行遍历,广度优先遍历的话需要借助queue来保存每一层变量的方式进行遍历。

1、深度优先遍历-前序遍历

1.1、递归

递归的思路遍历:
1、递归结束的条件就是root==nullptr
2、注意这个函数有一个返回值,这个递归代码怎么具体写呢?

/* 这里需要定一个函数,因此输出函数是有个vector<int>返回值的,所以需要另外写一个函数*/
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        preorder(root,result);
        return result;
    }

    void preorder(TreeNode* root,vector<int> &result){
        if(root == nullptr){
            return;
        }
        result.push_back(root->val);
        preorder(root->left,result);
        preorder(root->right,result);
    }

1.2、借助stack非递归

/*思路
1、借用stack
2、在压最左边栈的同时需要把其节点也输出。
3、然后再把指针指向右边节点,然后在循环。
*/
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        if(!root){
            return result;
        }
        stack<TreeNode*> s;
        TreeNode *cur = root;
        while(!s.empty()|| cur){
            while(cur){
                result.push_back(cur->val);
                s.push(cur);
                cur = cur->left;
            }
            /*退出则说明左边已经放完了,此时需要弹出栈顶元素,同时查看他的右节点,然后在继续循环*/
            cur = s.top();
            s.pop();
            cur = cur->right;
        }

        return result;
    }

2、深度优先遍历-中序遍历

2.1、递归

/* 这里需要定一个函数,因此输出函数是有个vector<int>返回值的,所以需要另外写一个函数*/
vector<int> preorderTraversal(TreeNode* root) {
    vector<int> result;
    preorder(root,result);
    return result;
}

void preorder(TreeNode* root,vector<int> &result){
    if(root == nullptr){
        return;
    }
    preorder(root->left,result);
    result.push_back(root->val);
    preorder(root->right,result);
}

2.2、借助stack非递归

/*思路:
1、用栈的方式来处理,不用递归。
2、栈的思想就是,不需要提前把root节点压入栈,而是在循环中才压入。同时因为是中序遍历,所以最先压栈的是按理说是右,再是root,再是左。但是这里在循环过程中,需要从root开始判断压入栈的左子树是否为空,不为空则一直把左边节点压入栈,也就是按照树的最左边从root到业务节点压入栈。此时叶子节点左子树为空,则此时弹出stack top元素,写入result数组中,此时弹出的元素就是最左边的叶子节点数据。然后此时需要拿着这个节点看看他的右节点。如果不为空,则需要把他的右节点压栈,并且指针指向他右节点的左孩子。如果右节点为空,则继续弹出stack top元素,写入result中,此时弹出的则是数的左边那条线倒数第二个节点。如果右节点存在,则将右节点压入栈,同时下一个指针指向来右节点的左节点,如果左节点为空,则弹出stack top元素(此时弹出的就是这个右节点)。指针就系指向p->right。此时为空则继续弹出stack。。。。循环继续。直到stack为空&&p指针同时为空,则说明占用没有来数据同时把树已经遍历完成(p==nulptr)
*/
/*思路:非递归。
1、用一个栈,但是不会想广度优先算法一样先把root放在queue中
2、把左子树一直放在stack中,直到坐左边的节点
3、然后为空周,再把右子树放进去,循环判断。*/
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        stack<TreeNode *> s;
        if(!root){
            return result;
        }
        TreeNode *p = root;
        /*注意里面不需要临时节点指针了。直接用p就行了。*/
        while(!s.empty()||p){

            /*这里是把最左边全部压入栈,直到最左边的叶子节点*/
            while(p){
                s.push(p);
                p = p->left;
            }

            /*把左边全部压入栈,直到最左边的节点压完,此时弹出坐左边节点。*/
            p = s.top();
            s.pop();
            result.push_back(p->val);
            /*再看看这个最左边节点的右节点是否为空
            为空,继续弹出剩下的最左边,否则将右节点对应的最左边全部压入栈,然后在重复上面的循环*/
            p = p->right;
        }
        return result;
    }

3、深度优先遍历-后序遍历

3.1、递归

/* 这里需要定一个函数,因此输出函数是有个vector<int>返回值的,所以需要另外写一个函数*/
vector<int> preorderTraversal(TreeNode* root) {
    vector<int> result;
    preorder(root,result);
    return result;
}

void preorder(TreeNode* root,vector<int> &result){
    if(root == nullptr){
        return;
    }
    preorder(root->left,result);
    preorder(root->right,result);
    result.push_back(root->val);
}

3.2、借助stack非递归

/*思路:
1、非递归方式通过stack,从右边一次放入stack,直到没有,则指针指向左子树,然后在循环放入栈,放入结果中,然后再把结果数组反序即可。*/
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> result;
        if(!root){
            return result;
        }
        stack<TreeNode*> s;
        TreeNode *cur = root;
        while(!s.empty()||cur){
            while(cur){
                result.push_back(cur->val);//输出
                s.push(cur);//压站
                cur = cur->right;
            }
            /*最右边压站完成,弹出顶元素,然后指向他的左孩子,继续循环压站*/
            cur = s.top();
            s.pop();
            cur = cur->left;
        }

        reverse(result.begin(),result.end());
        return result;
    }

4、广度优先遍历

4.1、递归

4.2、借助stack非递归

vector<vector<int>> levelOrder(TreeNode* root) {
    vector<vector<int>> result;
    if(!root){
        return result;
    }
    queue<TreeNode *> q;
    q.push(root);
    while(!q.empty()){
        int len = q.size();
        vector<int> value;
        for(int i = 0; i < len; i++){
            TreeNode *node = q.front();
            q.pop();
            if(node){
                value.push_back(node->val);
                q.push(node->left);
                q.push(node->right);
            }
        }
        if(value.size()){
            result.push_back(value);
        }
    }
    return result;
}

5、总结

深度优先遍历一般都会用stack实现,而广度优先算法一般都会用queue来实现。同时掌握来这些之后,通过这些原理就会衍生出需要有趣的题。而且二叉树的广度优先遍历应用比较广。比如按层打印每个节点,按照二叉树的层次Z字型打印节点,打印从二叉树右侧看到的二叉树的最右边节点。打印从二叉树最左侧看到的二叉树的节点。求二叉树的深度,最大深度,最小深度等等。这些都是可以在掌握了二叉树遍历方式后可以解决的。