# 二叉树遍历与构造

## 二叉树的遍历

  二叉树遍历其实很简单，三种遍历都是一样的，只不过顺序先后不一样罢了。

### 先序遍历

  访问根结点，先序遍历左子树，先序遍历右子树

```cpp
void PreOrder(BitTree T){
    if( T != nullptr){
        visit(T);
        PreOrder(T->lchild);
        PreOrder(T->rchild);
    }
}
```

### 中序遍历

  中序遍历左子树，访问根结点，中序遍历右子树

```cpp
void InOrder(BitTree T){
    if(T != nullptr){
        InOrder(T->lchild);
        visit(T);
        InOrder(T->rchild);
    }
}
```

### 后序遍历

后序遍历左子树，后序遍历右子树，访问根结点

```cpp
void PostOrder(BitTree T){
    if( T != nullptr ){
        PostOrder(T->lchild);
        PostOrder(T->rchild);
        visit(T);
    }
}
```

  时间复杂度都为O（n），在递归遍历中，递归工作栈的栈深恰好为树的深度，在最坏情况下，二叉树是有n个结点且深度为 n 的单支树，遍历算法的空间复杂度为O（n）。

### 利用非递归方式遍历二叉树

  借助栈，可以将二叉树的递归遍历转换为非递归算法。以中序遍历为例，给出中序遍历非递归算法。先扫描（并非访问）根结点的所有左结点并将它们一一进栈，然后出栈一个结点\*p，访问它。然后继续扫描该结点的右孩子结点，将其进栈，在扫描该孩子结点的所有左结点并一一进栈，如此继续，直到栈空。

  **中序遍历**的非递归算法如下：

```cpp
void InOrderWithoutRecursion(BitTree T){
    Stack<BitTree> S;           //辅助栈
    BitTree p = T;                //遍历指针
    while( p || !IsEmpty(S) ){    //栈不为空或p不为空时
        if( p ){                
            S.push(p);            //根指针进栈
            p = p->lchild;        //遍历左子树
        }else{
            S.pop(p);            //根指针退栈
            visit(p);            //cout << p->data;
            p = p->rchild;      //向右子树走
        }
    }    
}
```

  **前序遍历**的非递归算法：

```cpp
void PreOrderWithoutRecursion(BitTree T)
{
    if (T == nullptr)
        return;
    BTNode* p = T;
    stack<BitNode*> s;
    while (!s.empty() || p)
    {
        if (p)
        {
            visit(p);        // cout << p->data;
            s.push(p);
            p = p->lchild;
        }
        else
        {
            p = s.top();
            s.pop();
            p = p->rchild;
        }
    }
    cout << endl;
}
```

  **后序遍历**的非递归算法：后序遍历的难点在于：需要判断上次访问的节点是位于左子树，还是右子树。若是位于左子树，则需跳过根节点，先进入右子树，再回头访问根节点；若是位于右子树，则直接访问根节点。直接看代码，代码中有详细的注释。

```cpp
void PostOrderWithoutRecursion(BTNode* root)
{
    if (root == NULL)
        return;
    stack<BTNode*> s;
    //pCur:当前访问节点，pLastVisit:上次访问节点
    BTNode* pCur, *pLastVisit;
    //pCur = root;
    pCur = root;
    pLastVisit = NULL;
    //先把pCur移动到左子树最下边
    while (pCur)
    {
        s.push(pCur);
        pCur = pCur->lchild;
    }
    while (!s.empty())
    {
        //走到这里，pCur都是空，并已经遍历到左子树底端(看成扩充二叉树，则空，亦是某棵树的左孩子)
        pCur = s.top();
        s.pop();
        //一个根节点被访问的前提是：无右子树或右子树已被访问过
        if (pCur->rchild == NULL || pCur->rchild == pLastVisit)
        {
            cout << setw(4) << pCur->data;
            //修改最近被访问的节点
            pLastVisit = pCur;
        }
        /*这里的else语句可换成带条件的else if:
        else if (pCur->lchild == pLastVisit)//若左子树刚被访问过，则需先进入右子树(根节点需再次入栈)
        因为：上面的条件没通过就一定是下面的条件满足。仔细想想！
        */
        else
        {
            //根节点再次入栈
            s.push(pCur);
            //进入右子树，且可肯定右子树一定不为空
            pCur = pCur->rchild;
            while (pCur)
            {
                s.push(pCur);
                pCur = pCur->lchild;
            }
        }
    }
    cout << endl;
}
```

  **层次遍历**

  层次遍历需要借助队列。先将二叉树根结点入队，然后初岁，访问该结点，若它有左子树，则将左子树根节点入队；若它有右子树，则将右子树根结点入队。然后出队，对出队结点访问，如此反复，直到队列为空。

![](https://xiuxin-1304803037.cos.ap-shanghai.myqcloud.com/%E5%B1%82%E6%AC%A1%E9%81%8D%E5%8E%86.jpg)

```cpp
void LevelOrder(BitTree T){
    if( T == nullptr){
        return ;
    }
    Queue<BitNode*> q;                    //辅助队列
    q.push(T);                            //将根结点入队    
    while(!q.empty()){                    //队列不为空时
        int sz = q.size();                
        for(int i = 0; i < sz; i++){
            BitNode* cur = Q.front();    
            q.pop();                    //队头元素出队    
            if(p->lchild != nullptr){
                q.push(p->lchild);        //左子树不为空时，左子树入队
            }
            if(p->rchild != nullptr){
                q.push(p->rchild);        //右子树不为空时，右子树入队
            }
        }
    }
}
```

## 由遍历序列构造二叉树

  由二叉树的先序和中序序列可以唯一确定一棵二叉树，同样后序和中序序列也可以唯一确定一棵二叉树，但先序和后序不能确定一棵二叉树。

### **先序和中序序列构造一棵二叉树**

  思路：我们知道先序序列的第一个值其实就是根节点的值，这样我们就可以在中序序列中找到对应的根节点，那么根节点左边的数就全为左子树的结点，根节点右边的数就全为右子树的结点。根据这个想法，再递归的进行构造，见代码。

```cpp
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
private:
    unordered_map<int, int> index;  //借助哈希表，让我们快速的找到分割点，不用每次都去循环一遍
public:
    TreeNode* myBuildTree(const vector<int>& preorder, const vector<int>& inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
        if (preorder_left > preorder_right) {
            return nullptr;
        }
        // 前序遍历中的第一个节点就是根节点
        int preorder_root = preorder_left;
        // 在中序遍历中定位根节点
        int inorder_root = index[preorder[preorder_root]];

        // 先把根节点建立出来
        TreeNode* root = new TreeNode(preorder[preorder_root]);
        // 得到左子树中的节点数目
        int size_left_subtree = inorder_root - inorder_left;
        // 递归地构造左子树，并连接到根节点
        // 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
        root->left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
        // 递归地构造右子树，并连接到根节点
        // 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
        root->right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
        return root;
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n = preorder.size();
        // 构造哈希映射，帮助我们快速定位根节点
        for (int i = 0; i < n; ++i) {
            index[inorder[i]] = i;
        }
        return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
    }
};
```

### **后序和中序序列构造一棵二叉树**

  思路：我们知道后序序列的最后一个值其实就是根节点的值，这样我们就可以在中序序列中找到对应的根节点，那么根节点左边的数就全为左子树的结点，根节点右边的数就全为右子树的结点。根据这个想法，再递归的进行构造，见代码。

```cpp
class Solution {
private:
    unordered_map<int,int> index; //借助哈希表，让我们快速的找到分割点，不用每次都去循环一遍
public:
    TreeNode* myBuildTree(vector<int>& postorder,vector<int>& inorder,int postorder_left,int postorder_right,int inorder_left,int inorder_right){
        if(postorder_left > postorder_right){
            return nullptr;
        }
        //后序遍历的最后一个节点其实就是根节点
        int postorder_root = postorder_right;
        //在中序遍历中定位根节点
        int inorder_root = index[postorder[postorder_root]];
        //先建立根节点
        TreeNode* root=new TreeNode(postorder[postorder_root]);
        //得到左子树节点数量
        int size_left_subtree=inorder_root-inorder_left;
        //注意区间边界范围 +1，-1 一定要注意判别好
        //递归构造左右子树
        root->left=myBuildTree(postorder,inorder,postorder_left,postorder_left+size_left_subtree-1,inorder_left,inorder_root-1);
        root->right=myBuildTree(postorder,inorder,postorder_left+size_left_subtree,postorder_root-1,inorder_root+1,inorder_right);
        return root;
    }
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder){
        int n=postorder.size();
        //借助哈希表，让我们快速的找到分割点，不用每次都去循环一遍
        for(int i=0;i<n;i++){
            index[inorder[i]]=i;
        }
        return myBuildTree(postorder,inorder,0,n-1,0,n-1);
    }
};
```
