二叉树总结(二)树的遍历

该文我会用来总结二叉树相关的知识

二叉树如下图:

二叉树的结构

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

二叉树构造方法

为了测试二叉树的的各种算法,我不得不写一个二叉树的构造方法,我主要是用层次遍历的方式来构造二叉树的。层次遍历在后面会详细说到。

用字符串的方式来输入二叉树的序列,例如:

输入:1 2 3 4 5 6 7 null null null null null 8 null null

以上输入都是字符,最后两个null可省略。

TreeNode *createBinaryTree(vector<string> &arr){
    TreeNode *head = NULL;
    if (!arr.at(0).compare("null"))return head;//空树
    queue<TreeNode *>Q;
    vector<string>::iterator it = arr.begin();
    head = new TreeNode(stringToInteger(*it++));//stringToInteger将string转为int
    TreeNode *p = NULL;
    Q.push(head);//树根入队
    //队列不空,且没超过arr的大小(用于arr最后多余的null未列出来的情况,即上面说的可省略的情况)
    while (!Q.empty() && it != arr.end()){
        p = Q.front();//取出队首元素
        Q.pop();
        if (!(*it).compare("null")){//arr序列的下一个为空,则左子树为空
            p->left = NULL;
        }
        else{//否则生成左子树
            p->left = new TreeNode(stringToInteger(*it));
            Q.push(p->left);//左子树入队
        }
        ++it;
        if (it == arr.end())break;//不忘判断arr是否越界
        if (!(*it).compare("null")){//arr序列的下一个为空,则右子树为空
            p->right = NULL;
        }
        else{//否则生成右子树
            p->right = new TreeNode(stringToInteger(*it));
            Q.push(p->right);//右子树入队
        }
        ++it;
    }
    return head;
}

用数值的方式来输入二叉树的序列,例如:

输入:1 2 3 4 5 6 7 -1 -1 -1 -1 -1 8

默认树的所有值都是大于0的。

TreeNode *createBinaryTree(const vector<int> &arr){
    int index = 0;
    queue<TreeNode *>Q;
    TreeNode *head = new TreeNode(arr.at(index++));
    Q.push(head);
    TreeNode *p = NULL;
    while (index < arr.size() && !Q.empty()){
        p = Q.front();
        Q.pop();
        if (arr.at(index) >= 0){
            TreeNode *temp = new TreeNode(arr.at(index));
            p->left = temp;
            Q.push(temp);
        }
        ++index;
        if (index >= arr.size())break;
        if (arr.at(index) >= 0){
            TreeNode *temp = new TreeNode(arr.at(index));
            p->right = temp;
            Q.push(temp);
        }
        ++index;
    }
    return head;
}

二叉树遍历方法

前序遍历:1->2->4->5->3->6->8->7

中序遍历:4->2->5->1->8->6->3->7

后序遍历:4->5->2->8->6->7->3->1

层次遍历:1->2->3->4->5->6->7->8

前序递归实现:

vector<int> preorder(TreeNode* root){
    vector<int> retInt;
    if (root == NULL)return retInt;//空树
    retInt.push_back(root->val);//先访问值
    vector<int> left = preorder(root->left);//进入左子树
    retInt.insert(retInt.end(), left.begin(), left.end());//复制左子树的访问结果
    vector<int> right = preorder(root->right);//进入右子树
    retInt.insert(retInt.end(), right.begin(), right.end());
    return retInt;
}

前序费递归实现:

vector<int> preorder(TreeNode* root){
    vector<int>retInt;
    if (root == NULL)return retInt;//空树
    stack<TreeNode *>s;
    s.push(root);//树根入栈
    while (!s.empty()){
        TreeNode *p = s.top();
        retInt.push_back(p->val);//先访问值
        if (p->left != NULL){
            s.push(p->left);//遍历左子树,左子树入栈
        }
        else{//左子树为空
            s.pop();//当前节点出栈
            while (p->right == NULL && !s.empty()){//寻找非空右子树
                p = s.top();
                s.pop();
            }
            if (p->right != NULL)s.push(p->right);//右节点入栈
        }
    }
    return retInt;
}

中序递归实现:

vector<int> inorder(TreeNode* root){
    vector<int> retInt;
    if (root == NULL)return retInt;//空树
    retInt = inorder(root->left);//遍历左子树
    retInt.push_back(root->val);//访问当前节点值
    vector<int> temp = inorder(root->right);//遍历右子树
    retInt.insert(retInt.end(),temp.begin(),temp.end());//复制右子树的结果
    return retInt;
}

中序非递归实现:

vector<int> inorder(TreeNode* root){
    vector<int> retInt;
    if (root == NULL) return retInt;//空树
    TreeNode *p = NULL;
    stack<TreeNode *> s;
    s.push(root);//树根入栈
    while (!s.empty()){
        p = s.top();//获取栈顶元素
        if (p->left != NULL){
            s.push(p->left);//其左子树入栈
        }
        else{//左子树为空时
            s.pop();
            retInt.push_back(p->val);//访问其节点值
            while (p->right == NULL && !s.empty()){//寻找非空右子树
                p = s.top();//若右子树为空,获取新的栈顶元素
                retInt.push_back(p->val);//访问新元素的值
                s.pop();
            }
            if (p->right != NULL)s.push(p->right);//右子树入栈
        }
    }
    return retInt;
}

后序的递归实现:

vector<int> postorderTraversal2(TreeNode* root){
    vector<int> retInt;
    if (root == NULL)return retInt;//空树
    retInt = preorderTraversal2(root->left);//进入左子树
    vector<int> right = preorderTraversal2(root->right);//进入右子树
    retInt.insert(retInt.end(), right.begin(), right.end());
    retInt.push_back(root->val);//最后访问值
    return retInt;
}

后序的非递归实现:

 下面的方法需要增加数组记录已访问的所有节点,空间时间开销都比较大

bool LeetCode::checkVisitedPost(vector<TreeNode*>& visited, TreeNode* p){
    for (vector<int>::size_type i = 0; i < visited.size(); i++){
        if (visited.at(i) == p)return true;
    }
    return false;
}
vector<int> LeetCode::postorderTraversal(TreeNode* root){
    vector<int> retInt;
    if (root == nullptr)return retInt;//空树
    stack<TreeNode *>s;
    vector<TreeNode *>visited;//是否已访问过
    s.push(root);
    visited.push_back(root);
    while (!s.empty()){
        TreeNode* p = s.top();
        if (p->left && !checkVisitedPost(visited,p->left)){//左节点存在,且未访问过
            s.push(p->left);
            visited.push_back(p->left);
        }
        else if (p->right && !checkVisitedPost(visited, p->right)){//右节点存在,且未访问过
            s.push(p->right);
            visited.push_back(p->right);
        }
        else{
            retInt.push_back(p->val);//访问该节点
            s.pop();
        }
    }
    return retInt;
}

 下面的方法避开存储所有的已访问节点,而只存最后访问的右节点;时间空间开销都好很多;

vector<int> LeetCode::postorderTraversal(TreeNode* root){
    vector<int> retInt;
    if (root == nullptr)return retInt;//空树
    stack<TreeNode *>s;
    TreeNode* visited = nullptr;//记录前面已访问的树节点
    TreeNode* p = root;//记录当前节点
    while (p || !s.empty()){//!p保证根节点能入栈
        while (p){//左子树入栈
            s.push(p);
            p = p->left;
        }
        p = s.top();//获得最后的左节点
        if (!p->right || p->right == visited){
            retInt.push_back(p->val);//左右子树都已访问或为空,则访问当前节点
            visited = p;//记录已访问的右节点
            s.pop();
            p = nullptr;//置为空,防止重复遍历左子树
        }
        else p = p->right;
    }
    return retInt;
}

层次遍历的非递归实现:

使用队列保存每层的节点,另外为了知道每层的节点数量而增加了num数组。将每一层看做一个等级,每次要将遍历的节点的孩子节点加到对应的等级的数量中去。

vector<vector<int>> levelOrder(TreeNode* root){
    vector<vector<int>> ret;
    if (root == NULL)return ret;
    queue<TreeNode *>Q;
    Q.push(root);
    //表示当前的层次
    vector<int>::size_type level = 0;
    //当前层次的节点数量
    vector<int> num;
    num.push_back(1);
    while (!Q.empty()){
        TreeNode *p = Q.front();
        //判断当前等级对应的数组是否创建
        if (level < ret.size()){//已创建
            ret.at(level).push_back(p->val);
        }
        else{//未创建
            vector<int>nodei;
            nodei.push_back(p->val);
            ret.push_back(nodei);
        }
        Q.pop();
        --num.at(level);//数量减一
        if (p->left != NULL){//左子树入队
            Q.push(p->left);
            //判断当前等级对应的数量是否存在
            if (level < num.size() - 1){//已存在
                num.at(level + 1)++;
            }
            else{//不存在
                num.push_back(1);
            }
        }
        if (p->right != NULL){//右子树入队
            Q.push(p->right);
            if (level < num.size() - 1){
                num.at(level + 1)++;
            }
            else{
                num.push_back(1);
            }
        }
        if (num.at(level) <= 0){//当前层次等级的数量没有了,则进入下一层
            level++;
        }
    }
    return ret;
}

遍历的应用:

下面按照LeetCode中的题目来说明遍历的一些应用:

94 Binary Tree Inorder Traversal可用中序遍历来检查,二叉搜索树的中序遍历结果是递增的,这个按照上面的遍历算法就能简单解决。

100 Same Tree 判断两颗二叉树是否相同。遍历一遍,比较每个节点就可以了。

bool isSameTree(TreeNode* p, TreeNode* q){
    if (!p && !q)return true;//空树判断
    else if (!p || !q)return false;
    if (p->val != p->val)return false;//值比较
    return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}

101 Symmetric Tree 判断一颗二叉树是否镜像对称。两个指针按照先序遍历,一个按照根、左儿子、右儿子,一个按照根、右儿子、左儿子的顺序遍历,然后比较每个节点就可以了。注意,当遍历到根节点时,就可以停止,不需要遍历完整颗树。

递归的做法:

bool isSymmetric(TreeNode* root){
    if (!root)return true;
    return isSymmetricRecur(root->left, root->right);
}

bool isSymmetricRecur(TreeNode* left, TreeNode*right){
    if (!left && !right)return true;
    else if (!left || !right)return false;
    if (left->val != right->val)return false;
    return isSymmetricRecur(left->left, right->right) && isSymmetricRecur(left->right, right->left);
}

非递归的做法:

bool isSymmetric(TreeNode* root){
    if (!root)return true;
    TreeNode *p = root->left, *q = root->right;
    stack<pair<TreeNode *, TreeNode *>>S;
    while (!S.empty() || p || q){//注意:循环条件是!S.empty() || p || q,因为q、p可能为空
        if ((!p && q)|| (!q && p))return false;//仅其中一个为空
        if (p && q){
            if (p->val != q->val)return false;//值不等,放到里面不用判断p,q是否为空
            S.push({ p, q });
            p = p->left;
            q = q->right;
        }
        else{
            p = S.top().first;
            q = S.top().second;
            S.pop();
            if (p == q)break;//树根
            p = p->right;
            q = q->left;
        }
    }
    return true;
}

102 Binary Tree Level Order Traversal 输出二叉树的层次遍历的结果,要求每层的结果一个集合。上面层次遍历的算法就是这题的答案。

103 Binary Tree Zigzag Level Order Traversal 按照"之"字形来遍历每层,输出遍历的结果,要求每层的结果一个集合。

层次遍历的算法,注意奇数层(左->右)和偶数层(右->左)的输出方向不一样就可以了。

vector<vector<int>> LeetCode::zigzagLevelOrder(TreeNode* root){
    vector<vector<int>> ret;
    if (root == NULL)return ret;
    queue<TreeNode *>Q;
    Q.push(root);
    //表示当前的层次
    vector<int>::size_type level = 0;
    //当前层次的节点数量
    vector<int> num;
    num.push_back(1);
    stack<int>s;
    while (!Q.empty()){
        TreeNode *p = Q.front();
        //等级为奇数时,要先入栈
        if (level % 2){
            s.push(p->val);
        }
        else{
            //判断当前等级对应的数组是否创建
            if (level < ret.size()){//已创建
                ret.at(level).push_back(p->val);
            }
            else{//未创建
                vector<int>nodei;
                nodei.push_back(p->val);
                ret.push_back(nodei);
            }
        }
        Q.pop();
        --num.at(level);//数量减一
        if (p->left != NULL){//左子树入队
            Q.push(p->left);
            //判断当前等级对应的数量是否存在
            if (level < num.size() - 1){//已存在
                num.at(level + 1)++;
            }
            else{//不存在
                num.push_back(1);
            }
        }
        if (p->right != NULL){//右子树入队
            Q.push(p->right);
            if (level < num.size() - 1){
                num.at(level + 1)++;
            }
            else{
                num.push_back(1);
            }
        }
        if (num.at(level) <= 0){//当前层次等级的数量没有了,则进入下一层
            if (level % 2){
                if (level >= ret.size()){//若没有当前层的节点数组,则建立一个
                    vector<int>nodei;
                    nodei.push_back(s.top());
                    ret.push_back(nodei);
                    s.pop();
                }
                while (!s.empty()){//将栈中的元素加入到当前层次的数组中,实现反序
                    ret.at(level).push_back(s.top());
                    s.pop();
                }
            }
            level++;
        }
    }
    return ret;
}

 104 Maximum Depth of Binary Tree 求二叉树的深度

 递归解法:

int maxDepthRecur(TreeNode* root){
    if (!root)return 0;
    return 1 + max(maxDepthRecur(root->left), maxDepthRecur(root->right));
}

非递归解法

int maxDepth(TreeNode* root){
    if (!root)return 0;
    int dep = 0, res = 0;
    TreeNode *p = root;
    stack<pair<TreeNode *, int>>S;
    while (!S.empty() || p){
        if (p){
            ++dep;
            res = max(res, dep);
            S.push({ p, dep });
            p = p->left;
        }
        else{
            p = S.top().first;
            dep = S.top().second;
            S.pop();
            p = p->right;
        }
    }
    return res;
}

105 Construct Binary Tree from Preorder and Inorder Traversal 通过前序和中序序列构造二叉树

106 Construct Binary Tree from Inorder and Postorder Traversal 通过后序和中序序列构造二叉树

上面两题解法参考 http://www.cnblogs.com/yeqluofwupheng/p/7429781.html

107 Binary Tree Level Order Traversal II
110 Balanced Binary Tree
111 Minimum Depth of Binary Tree
112 Path Sum
113 Path Sum II
114 Flatten Binary Tree to Linked List
116 Populating Next Right Pointers in Each Node
117 Populating Next Right Pointers in Each Node II
124 Binary Tree Maximum Path Sum
129 Sum Root to Leaf Numbers
144 Binary Tree Preorder Traversal
145 Binary Tree Postorder Traversal
156 Binary Tree Upside Down
199 Binary Tree Right Side View
222 Count Complete Tree Nodes
226 Invert Binary Tree
236 Lowest Common Ancestor of a Binary Tree
250 Count Univalue Subtrees
257 Binary Tree Paths
297 Serialize and Deserialize Binary Tree
298 Binary Tree Longest Consecutive Sequence
337 House Robber III
366 Find Leaves of Binary Tree
404 Sum of Left Leaves
437 Path Sum III
508 Most Frequent Subtree Sum
513 Find Bottom Left Tree Value
515 Find Largest Value in Each Tree Row
536 Construct Binary Tree from String
543 Diameter of Binary Tree
545 Boundary of Binary Tree
549 Binary Tree Longest Consecutive Sequence II
563 Binary Tree Tilt
572 Subtree of Another Tree
582 Kill Process
606 Construct String from Binary Tree
617 Merge Two Binary Trees
623 Add One Row to Tree
637 Average of Levels in Binary Tree
652 Find Duplicate Subtrees
654 Maximum Binary Tree
655 Print Binary Tree
662 Maximum Width of Binary Tree
663 Equal Tree Partition

LeetCode的Sum Root to Leaf Numbers可用后序遍历来解决。 

LeetCode的Binary Tree Right Side View可用后序遍历来解决。 

原文地址:https://www.cnblogs.com/yeqluofwupheng/p/6661399.html