二叉树各种操作的总结

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

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

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. 判断两棵二叉树是否结构相同

递归实现:先判断根是否相同,再判断左子树和右子树是否相同

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

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;
}

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

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

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;
}

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

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

    4
1       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; // 左不满足
    }
}

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

递归解法:

(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;
}

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

二叉树中节点的最大距离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));
}

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

前序的开始是根;中序的一个节点的左侧是左子树,右侧是右子树。

因此先从前序中确定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;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-15 07:06:26

二叉树各种操作的总结的相关文章

二叉树插入操作

/* 功能:将一个节点插入到二叉树中节点的子节点中 输入: p,c ,RL p:节点将插入到p所指向的节点的子节点中 c:指向待插入的节点 RL:为0表示插入到左子树,为1表示插入到右子树 输出:bool */ template<typename T> bool BinaryTree<T>::InsertChild(BTNode<T> *p,BTNode<T> *c,int RL) { if(p) { if(RL==0) //插入到p的左节点 { c->

搜索二叉树的操作

搜索二叉树的数据结构定义: /*二叉搜索树的结构定义*/ typedef struct TreeNode* SearchTree; typedef struct TreeNode* Position; struct TreeNode { int Element; SearchTree Left; SearchTree Right; } 搜索二叉树的插入操作: SearchTree Insert(int x, SearchTree T) { if(T == NULL)//空树 { T = mall

二叉树的操作之统计二叉树中节点的个数

一,问题描述 给定一颗二叉树,已知其根结点. ①计算二叉树所有结点的个数 ②计算二叉树中叶子结点的个数 ③计算二叉树中满节点(度为2)的个数 二,算法分析 找出各个问题的基准条件,然后采用递归的方式实现. ①计算二叉树所有结点的个数 1)当树为空时,结点个数为0,否则为根节点个数 加上 根的左子树中节点个数 再加上 根的右子树中节点的个数 借助遍历二叉树的思路,每访问一个结点,计数增1.因此,可使用类似于先序遍历的思路来实现,代码如下: //计算树中节点个数 private int nubmer

c语言(二叉树的操作)

#include "stdio.h" #include "malloc.h" #include "stdlib.h" typedef struct BTNode { int data; struct BTNode *Lchild,*Rchild; }BTree; //初始化 BTree * Ini_BTNode() { BTree *bt ; int a; bt=(BTree *)malloc(sizeof(BTree)); printf(&qu

数据结构——二叉树的操作

这里我们主要讲二叉排序树的操作: 什么是二叉排序树? 或者是一棵空树 或者是具有一下性质的二叉树: a.若它的左子树不空,则左子树上所有结点的值均小于它的根节点的值: b.若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值: c.它的左.右子树也分别为二叉排序树 Tip : 中序(左根右)遍历二叉排序树会得到一个关键字的递增有序序列 二叉排序树的操作--查找 查找步骤: 若查找的关键字等于根节点,成功 若查找的关键字小于根节点,查找其左子树 若查找的关键字大于根节点,查找其右子树 在左

二叉树的操作

在计蒜客上学了二叉树,感觉自己还学了点东西,就贴在这里吧 #include<iostream> #include<string> using namespace std; class Node { public: char data; Node *lchild, *rchild; Node(int _data) { data = _data; lchild = NULL; rchild = NULL; } ~Node() { if (lchild != NULL) { delete

C++实现二叉树相关操作

测试环境:windows 7 vs2010 主要实现二叉树的初始化递归和非递归遍历,层次遍历,获取叶子节点的个数,获取树的高度以及镜像树,部分代码也参考了互联网上的相关资料. 源程序: BinaryTreeNode.h #ifndef _BINARY_NODE #define _BINARY_NODE #include<iostream> using namespace std; template<class ItemType> struct BinaryTreeNode { It

c之二叉树链表操作---建立、(递归)前序遍历、中序遍历、后序遍历

[二叉树链表] 1.节点定义: typedef struct node{ int data; struct node*lchild,*rchild; }Tree,*BiTree; 2.创建二叉树: BiTree creat_Tree(BiTree root,int num){//建立二叉树 if(root==NULL) { root=(Tree *)malloc(sizeof(Tree)); if(root==NULL) { printf("no memory available\n"

华南理工数据结构大作业第二题 二叉树各种操作深度结点个数后序前序中序层次求祖先

/*#include<iostream> #include<windows.h> using namespace std ; struct BTNode { char data ; BTNode *left ; BTNode *right ; BTNode () { left = NULL ; right = NULL ; } } ; int main () { cout <<"题目所给的二叉树用括号表示法后表示为:A(B(D,E(H(J,K(L,M(,N))