二叉树各种操作的总结

来源:互联网 发布:阿里云学生服务器 编辑:程序博客网 时间:2024/06/05 10:07

本篇文章部分参考轻松搞定面试中的二叉树题目实现。

    • 求二叉树中的节点个数
    • 求二叉树中叶子节点的个数
    • 求二叉树的深度
    • 求二叉树第K层的节点个数
    • 递归遍历前序中序后序
    • 非递归遍历前序中序后序层序
      • 1 前序遍历
      • 2 中序遍历
      • 3 后序遍历
      • 4 层序遍历
    • 将二叉查找树变为有序的双向链表
    • 求二叉树的镜像
    • 判断两棵二叉树是否结构相同
    • 判断二叉树是不是平衡二叉树
    • 判断二叉树是否是搜索二叉树
    • 判断二叉树是不是完全二叉树
    • 求二叉树中两个节点的最低公共祖先节点
    • 求二叉树中节点的最大距离
    • 由前序遍历序列和中序遍历序列重建二叉树

所有源码:https://github.com/qzxin/BinaryTree/blob/master/binary-tree-all.cpp

1. 求二叉树中的节点个数

递归求解,二叉树的节点个数等于 左子树的个数+右子树的个数+1(根)

//求二叉树中的节点个数int GetNums(BinaryTreeNode* root) {    if (root == NULL)        return 0;    return GetNums(root->m_pLeft) + GetNums(root->m_pRight) + 1;}

求二叉树中叶子节点的个数

叶子节点的定义是:自身非空,左右为NULL;
和求二叉树中的节点个数类似,只是必须满足左右为NULL,才算1个

int GetLeafNodeNums(BinaryTreeNode* root) {    if (root == NULL)        return 0;    if (root->m_pLeft == NULL && root->m_pRight == NULL)        return 1;    int leftNums = GetLeafNodeNums(root->m_pLeft);    int rightNums = GetLeafNodeNums(root->m_pRight);    return leftNums+rightNums;}

2. 求二叉树的深度

递归求解,二叉树的深度等于 左右子树深度中的最大值+1(根)

// 求二叉树的深度int GetDepth(BinaryTreeNode* root) {    if (root == NULL)        return 0;    return max(GetDepth(root->m_pLeft), GetDepth(root->m_pRight)) + 1;}

3. 求二叉树第K层的节点个数

递归实现
k = 1, nums = 1;
二叉树第K层的节点个数 = 左子树K-1层的节点个数 + 右子树K-1层的节点个数

// 求二叉树第K层的节点个数int GetNLevelNums(BinaryTreeNode* root, int k) {    if (root == NULL || k == 0)        return 0;    if (k == 1)        return 1;    // 左右子树k-1层节点数的和    return GetNLevelNums(root->m_pLeft, k-1) + GetNLevelNums(root->m_pRight, k-1);}

4 . 递归遍历:前序,中序,后序

// 递归遍历:前序遍历,中序遍历,后序遍历void visit(BinaryTreeNode* root) {    cout << root->m_val << " ";}void PreOrderTravel(BinaryTreeNode* root) {    if (root == NULL)        return;    visit(root);    PreOrderTravel(root->m_pLeft);    PreOrderTravel(root->m_pRight);}void InOrderTravel(BinaryTreeNode* root) {    if (root == NULL)        return;    InOrderTravel(root->m_pLeft);    visit(root);    InOrderTravel(root->m_pRight);}void PostOrderTravel(BinaryTreeNode* root) {    if (root == NULL)        return;    PostOrderTravel(root->m_pLeft);    PostOrderTravel(root->m_pRight);    visit(root);}

5. 非递归遍历:前序,中序,后序,层序

5.1 前序遍历

先访问根,然后压右节点,左节点进栈。(访问时顺序则是,根,左,右)

void PreOrderTravel(BinaryTreeNode* root) {    if (root == NULL)        return;    stack<BinaryTreeNode*> s;    s.push(root);    while (s.empty() == false) {        root = s.top();        visit(root);        s.pop();        if (root->m_pRight)            s.push(root->m_pRight);        if (root->m_pLeft)            s.push(root->m_pLeft);    }}

5.2 中序遍历

先入栈后访问。因为中序首先要访问左节点,所以要压到树的最后一个左节点,才开始访问;最后一个左节点没有左节点(相当于根),之后开始遍历它的右子树。

void InOrderTravelRecur(BinaryTreeNode* root) {    if (root == NULL)        return;    stack<BinaryTreeNode*> s;    BinaryTreeNode* node  = root;    while (node != NULL || s.empty() == false) {        if (node) {            s.push(node);            node = node->m_pLeft;        } else {            node = s.top();            s.pop();            visit(node);            node = node->m_pRight;        }    }}

5.3 后序遍历

用2个栈实现,

// 辅助栈s1:压根,压左,压右; // 栈s2:   压根,压右,压左;(出栈访问则为:左,右,根)void PostOrderTravelRecur(BinaryTreeNode* root) {    if (root == NULL)        return;    stack<BinaryTreeNode*> s1;    stack<BinaryTreeNode*> s2;    BinaryTreeNode* node = root;    s1.push(node);    while (s1.empty() == false) {        node = s1.top();        s1.pop();        s2.push(node); //根压入s2        if (node->m_pLeft)            s1.push(node->m_pLeft);// 左 压入 s1        if (node->m_pRight)            s1.push(node->m_pRight); // 右 压入 s1    }    while (s2.empty() == false) {        node = s2.top();        s2.pop();        visit(node);    }}

5.4 层序遍历

逐层访问,先入先出,使用队列实现。访问根,然后将左、右入队列

void LevelOrderTravel(BinaryTreeNode* root) {    if (root == NULL)        return;    queue<BinaryTreeNode*> q;    q.push(root);    while (q.empty() == false) {        root = q.front();        visit(root);        q.pop();        if (root->m_pLeft)            q.push(root->m_pLeft);        if (root->m_pRight)            q.push(root->m_pRight);    }}

6. 将二叉查找树变为有序的双向链表

递归实现,二叉树可以分为左子树、根、右子树
设双向链表的头尾是pFirst, pLast

  • 根为空: pFirst, pLast为NULL
  • 处理左子树
    • 左子树空:则根为双向链表的头部
    • 左子树非空:左子树双向链表的头,是最终双向链表的头;左子树双向链表的尾和根相连;
  • 处理右子树
    • 右子树空:根为双向链表的尾部
    • 右子树非空: 右子树双向链表的尾,是最终双向链表的尾;右子树双向链表的头和根相连;

因此,除了声明最终双向链表的头尾,还要声明左、右子树转换成双向链表的头尾。

// 将二叉查找树变为有序的双向链表void CovertToList(BinaryTreeNode* root, BinaryTreeNode* &pFirst, BinaryTreeNode* &pLast) {    BinaryTreeNode* pLeftFirst(NULL), *pLeftLast(NULL), *pRightFirst(NULL), *pRightLast(NULL);    if (root == NULL) {        pFirst = NULL;        pLast = NULL;        return;    }    if (root->m_pLeft == NULL) {        pFirst = root;    } else {        CovertToList(root->m_pLeft, pLeftFirst, pLeftLast);        pLeftLast->m_pRight = root;         root->m_pLeft = pLeftLast;        pFirst = pLeftFirst;    }    if (root->m_pRight == NULL) {        pLast = root;    } else {        CovertToList(root->m_pRight, pRightFirst, pRightLast);        pRightFirst->m_pLeft = root;        root->m_pRight = pRightFirst;        pLast = pRightFirst;    }}

7. 求二叉树的镜像

递归的交换树的左右子树。

// 求二叉树的镜像BinaryTreeNode* MirrorTree(BinaryTreeNode* root) {    if (root == NULL)        return NULL;    BinaryTreeNode* pLeft = MirrorTree(root->m_pLeft);    BinaryTreeNode* pRight = MirrorTree(root->m_pRight);    root->m_pLeft = pRight;    root->m_pRight = pLeft;    return root;}

8. 判断两棵二叉树是否结构相同

递归实现:先判断根是否相同,再判断左子树和右子树是否相同
// 判断两棵二叉树是否结构相同

bool TreeStructCmp(BinaryTreeNode* root1, BinaryTreeNode* root2) {    if (root1 == NULL && root2 == NULL)        return true;    if (root1 != root2)        return false;    bool leftResult = TreeStructCmp(root1->m_pLeft, root2->m_pLeft);    bool rightResult = TreeStructCmp(root1->m_pRight, root2->m_pRight);    return leftResult && rightResult;}

9. 判断二叉树是不是平衡二叉树

只有每一个子树都是平衡树,才能保证它是平衡二叉树。因此,先判断左右子树是否是平衡的并记录左右子树的深度,再判断树是否平衡

bool IsAVL(BinaryTreeNode* root) {    int depth;    return IsAVLTree(root, depth);}// 后序遍历,只访问1次节点bool IsAVLTree(BinaryTreeNode* root, int &depth) {    if (root == NULL) {        depth = 0;        return true;    }     int leftDepth, rightDepth;    // 后序遍历,先判断左右子树是否是平衡的,再判断树是否平衡    if (IsAVLTree(root->m_pLeft, leftDepth) && IsAVLTree(root->m_pRight, rightDepth)) {        if (abs(leftDepth-rightDepth) <= 1) {            depth = max(leftDepth, rightDepth)+1;            return true;        }    }    return false;}

10. 判断二叉树是否是搜索二叉树

注意:仅通过比较左节点 <= 根 <= 右节点 不能判断这是一个搜索二叉树,因为这不能保证右子树的所有节点值都大于左子树中的所有节点。如下图所示,它不是BST

    41       5     0     6

通过中序遍历(从小到大的顺序)来判断是否是一个搜索二叉树

// 判断二叉树是否是搜索二叉树bool IsBST(BinaryTreeNode* root) {    int prev = INT_MIN; // 比较元素,设为最小    return IsBSTreeHelper(root, prev);}// 中序遍历,判断遍历顺序是否是从小到大bool IsBSTreeHelper(BinaryTreeNode* root, int &prev) {    if (root == NULL)        return true;    if (IsBSTreeHelper(root->m_pLeft, prev)) {        if (root->m_val >= prev) {            prev = root->m_val;            if (IsBSTreeHelper(root->m_pRight, prev))                return true; // 此时左子树、根、右子树都满足要求            else                return false; // 右不满足        } else {            return false; // 根不满足        }    } else {        return false; // 左不满足    }}

11. 判断二叉树是不是完全二叉树

完全二叉树,要求在最后一层的右侧才可以存在空节点。
层序遍历,一旦一个节点含有空子树后,之后所有的节点必须只含有NULL。

  • 不含有空子树时,按层序将当前节点的左右子树压入队列
  • 一旦含有空子树时,当前节点和队列中后续节点的左右子树,必须为空,才是完全二叉树。
/ 判断二叉树是不是完全二叉树// 层序遍历,一旦一个节点含有空子树后,之后所有的节点必须只含有NULLbool IsCompleteBinaryTree(BinaryTreeNode* root) {    if (root == NULL)        return true;    queue<BinaryTreeNode* > q;    bool HaveNULL = false;    q.push(root);    while (q.empty() == false) {        root = q.front();        q.pop();        if (HaveNULL) {            if (root->m_pLeft || root->m_pRight)                return false;        } else {            if (root->m_pLeft && root->m_pRight) {                // 左右非空                q.push(root->m_pLeft);                q.push(root->m_pRight);            } else if (root->m_pLeft && root->m_pRight == NULL) {                // 左非空,右空                HaveNULL = true;                q.push(root->m_pLeft);            } else if (root->m_pLeft == NULL && root->m_pRight) {                // 左空,右非空                return false;            } else {                // 左右为空                HaveNULL = true;            }        }    }    return true;}

12. 求二叉树中两个节点的最低公共祖先节点

递归解法:
(1)如果两个节点,1个在左子树,1个在右子树,则最低公共祖先节点是根
(2)如果两个节点都在左子树,递归处理左子树;反之处理右子树

在树中寻找 node1 ,并保存路径,如果没找到node1,递归把路径的节点pop;
在树中寻找 node2 ,并保存路径,如果没找到node1,递归把路径的节点pop;
如果有1个没有找到,则返回NULL,如果都找到,则从2条路径的开始,向后遍历,找到最后一个共同的节点。

// 求二叉树中两个节点的最低公共祖先节点BinaryTreeNode* GetLastCommonParent(BinaryTreeNode* root, BinaryTreeNode* node1, BinaryTreeNode* node2) {    if (root == NULL || node1 == NULL || node2 == NULL)        return NULL;    list<BinaryTreeNode*> path1, path2;    bool found1 = GetPath(root, node1, path1);    bool found2 = GetPath(root, node2, path2);    BinaryTreeNode* pLast = NULL;    if (!found1 || !found2)        return NULL;    // 找到路径中最后一个共同节点    list<BinaryTreeNode*>::const_iterator iter1 = path1.begin();    list<BinaryTreeNode*>::const_iterator iter2 = path2.begin();    while (iter1 != path1.end() && iter2 != path2.end()) {        if (*iter1 != *iter2)            break;        pLast = *iter1;        iter1++;        iter2++;    }    return pLast;}// 获得根到该节点的路径bool GetPath(BinaryTreeNode* root, BinaryTreeNode* node, list<BinaryTreeNode*> &path) {    if (root == node) {        path.push_back(root);        return true;    }    if (root == NULL)        return false;    bool found = false;    path.push_back(root); // 路径从根开始    found = GetPath(root->m_pLeft, node, path); //左子树中查找    if (!found)        found = GetPath(root->m_pRight, node, path); // 右子树中查找    if (!found)        path.pop_back(); // 此子树中没找到,pop出已经压入的此子树的节点    return found;}

13. 求二叉树中节点的最大距离

二叉树中节点的最大距离maxDistance指,任意两个节点相连的路径长度的最大值。

  • 树为空,距离为0;
  • 树非空,最大距离有3种可能,左子树中的最大距离,右子树中的最大距离,从左子树到根然后到右子树的最大距离
int GetMaxDistance(BinaryTreeNode* root) {    int maxLeft = 0, maxRight = 0; // 左右子树到根的最大距离    return GetMaxDistRecur(root, maxLeft, maxRight); // 在计算最大距离时,记录左右子树到根的最大距离}//maxLeft, maxRight 左右子树中的节点到根的最大距离int GetMaxDistRecur(BinaryTreeNode* root, int &maxLeft, int &maxRight) {    if (root == NULL) {        maxLeft = 0, maxRight = 0;        return 0;    }    int maxLL, maxLR, maxRL, maxRR;    int maxDistLeft, maxDistRight; // 左、右子树中的最大距离    if (root->m_pLeft) {        maxDistLeft = GetMaxDistRecur(root->m_pLeft, maxLL, maxLR);        maxLeft = max(maxLL, maxRR) + 1;    } else {        maxDistLeft = 0;        maxLeft = 0;    }    if (root->m_pRight) {        maxDistRight = GetMaxDistRecur(root->m_pRight, maxRL, maxRR);        maxRight = max(maxRL, maxRR) + 1;    } else {        maxDistRight = 0;        maxRight = 0;    }    return max(maxLeft+maxRight, max(maxDistLeft, maxDistRight));}

14. 由前序遍历序列和中序遍历序列重建二叉树

前序的开始是根;中序的一个节点的左侧是左子树,右侧是右子树。
因此先从前序中确定1个根,然后去中序中找到这个根,确定属于该根的左右子树的范围。递归的处理左右子树。

// 由前序遍历序列和中序遍历序列重建二叉树BinaryTreeNode* RebuildBinaryTree(int* preorder, int* inorder, int nums) {    if (preorder == NULL || inorder == NULL || nums <= 0)        return NULL;    BinaryTreeNode* root = new BinaryTreeNode(preorder[0]); // 根    int rootPositionOnInorder = -1;    for (int i = 0; i < nums; i++) {        if (inorder[i] == root->m_val) {            rootPositionOnInorder = i; // 中序中寻找根            break;        }    }    if (rootPositionOnInorder == -1) {        cout << "Input Error." << endl;    }    // Rebuild Left Tree    root->m_pLeft = RebuildBinaryTree(preorder+1, inorder, rootPositionOnInorder);    // Rebuild Right Tree    root->m_pRight = RebuildBinaryTree(preorder+1+rootPositionOnInorder, inorder+1+rootPositionOnInorder, nums-rootPositionOnInorder-1);    return root;}
0 0
原创粉丝点击