二叉树

来源:互联网 发布:美乎机油广州门市部 编辑:程序博客网 时间:2024/06/07 23:06

二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。

遍历顺序

遍历是对树的一种最基本的运算,所谓遍历二叉树,就是按一定的规则和顺序走遍二叉树的所有结点,使每一个结点都被访问一次,而且只被访问一次。由于二叉树是非线性结构,因此,树的遍历实质上是将二叉树的各个结点转换成为一个线性序列来表示。
设L、D、R分别表示遍历左子树、访问根结点和遍历右子树, 则对一棵二叉树的遍历有三种情况:DLR(称为先根次序遍历),LDR(称为中根次序遍历),LRD (称为后根次序遍历)。
 
先序遍历
首先访问根,再先序遍历左(右)子树,最后先序遍历右(左)子树。
 
中序遍历
首先中序遍历左(右)子树,再访问根,最后中序遍历右(左)子树。
 
后序遍历
首先后序遍历左(右)子树,再后序遍历右(左)子树,最后访问根。
 
二叉树节点定义如下:
struct BinaryTreeNode
{
    int m_nValue;
    BinaryTreeNode* m_pLeft;
    BinaryTreeNode* m_pRight;
};

题目列表:

1. 求二叉树中的节点个数
2. 求二叉树的深度
3. 前序遍历,中序遍历,后序遍历
4.分层遍历二叉树(按层次从上往下,从左往右)
5. 将二叉查找树变为有序的双向链表
6. 求二叉树第K层的节点个数
7. 求二叉树中叶子节点的个数
8. 判断两棵二叉树是否结构相同
9. 判断二叉树是不是平衡二叉树
10. 求二叉树的镜像
11. 求二叉树中两个节点的最低公共祖先节点
12. 求二叉树中节点的最大距离
13. 由前序遍历序列和中序遍历序列重建二叉树
14.判断二叉树是不是完全二叉树

 

详细解答:

1. 求二叉树中的节点个数
 递归解法:
(1)如果二叉树为空,节点个数为0
(2)如果二叉树不为空,二叉树节点个数 = 左子树节点个数 + 右子树节点个数 + 1
参考代码如下:

<strong>int GetNodeNum(BinaryTreeNode * pRoot){    if(pRoot==NULL)  //递归出口 -- 结束递归           return 0;return GetNodeNum(pRoot->m_pLeft) + GetNodeNum(pRoot->m_pRight) + 1; }</strong>

2. 求二叉树的深度

递归解法:
(1)如果二叉树为空,二叉树的深度为0
(2)如果二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1
参考代码如下:

<strong>class test{int GetDepth(BinaryTreeNode * pRoot){if(pRoot == NULL) // 递归出口return 0;int depthLeft = GetDepth(pRoot->m_pLeft);//左子树的深度int depthRight = GetDepth(pRoot->m_pRight);//右子树的深度//二叉树的深度,取左右中大的那个值。+1为根节点return depthLeft > depthRight ? (depthLeft + 1) : (depthRight + 1); }};</strong>

3. 前序遍历,中序遍历,后序遍历
 前序遍历递归解法:
(1)如果二叉树为空,空操作
(2)如果二叉树不为空,访问根节点,前序遍历左子树,前序遍历右子树
参考代码如下:

void PreOrderTraverse(BinaryTreeNode * pRoot){if(pRoot == NULL)return;<span style="color:#ff6666;">Visit(pRoot); </span>// 访问根节点PreOrderTraverse(pRoot->m_pLeft); // 前序遍历左子树PreOrderTraverse(pRoot->m_pRight); // 前序遍历右子树}

中序遍历递归解法
(1)如果二叉树为空,空操作。
(2)如果二叉树不为空,中序遍历左子树,访问根节点,中序遍历右子树
参考代码如下:

<strong>void InOrderTraverse(BinaryTreeNode * pRoot){if(pRoot == NULL)return;InOrderTraverse(pRoot->m_pLeft); // 中序遍历左子树<span style="color:#ff6666;">Visit(pRoot); </span>// 访问根节点InOrderTraverse(pRoot->m_pRight); // 中序遍历右子树}</strong>

后序遍历递归解法
(1)如果二叉树为空,空操作
(2)如果二叉树不为空,后序遍历左子树,后序遍历右子树,访问根节点
参考代码如下:

<strong>void PostOrderTraverse(BinaryTreeNode * pRoot){if(pRoot == NULL)return;PostOrderTraverse(pRoot->m_pLeft); // 后序遍历左子树PostOrderTraverse(pRoot->m_pRight); // 后序遍历右子树<span style="color:#ff6666;">Visit(pRoot);</span> // 访问根节点}</strong>

4.分层遍历二叉树(按层次从上往下,从左往右)

相当于广度优先搜索,使用队列实现。队列初始化,将根节点压入队列。当队列不为空,进行如下操作:弹出一个节点,访问,若左子节点或右子节点不为空,将其压入队列。

<p> void LevelTraverse(BinaryTreeNode * pRoot)  {  if(pRoot==NULL)   return ;  queue<BinaryTreeNode *>q;  q.push(pRoot);  while(!q.empty())  {   BinaryTreeNode *pNode =q.front();// front   q.pop();   Visit(pNode);//访问结点</p><p>    if(pNode->m_pLeft != NULL)         q.push(pNode->m_pLeft);          if(pNode->m_pRight != NULL)        q.push(pNode->m_pRight);    }  return; }</p>

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

要求不能创建新节点,只调整指针。
递归解法:
(1)如果二叉树查找树为空,不需要转换,对应双向链表的第一个节点是NULL,最后一个节点是NULL
(2)如果二叉查找树不为空:

如果左子树为空,对应双向有序链表的第一个节点是根节点,左边不需要其他操作;
如果左子树不为空,转换左子树,二叉查找树对应双向有序链表的第一个节点就是左子树转换后双向有序链表的第一个节点,同时将根节点和左子树转换后的双向有序链 表的最后一个节点连接;
如果右子树为空,对应双向有序链表的最后一个节点是根节点,右边不需要其他操作;
如果右子树不为空,对应双向有序链表的最后一个节点就是右子树转换后双向有序链表的最后一个节点,同时将根节点和右子树转换后的双向有序链表的第一个节点连 接。

参考代码如下:

/******************************************************************************参数:pRoot: 二叉查找树根节点指针pFirstNode: 转换后双向有序链表的第一个节点指针pLastNode: 转换后双向有序链表的最后一个节点指针******************************************************************************/void Convert(BinaryTreeNode * pRoot,              BinaryTreeNode * & pFirstNode, BinaryTreeNode * & pLastNode){BinaryTreeNode *pFirstLeft, *pLastLeft, * pFirstRight, *pLastRight;if(pRoot == NULL) {pFirstNode = NULL;pLastNode = NULL;return;}if(pRoot->m_pLeft == NULL){// 如果左子树为空,对应双向有序链表的第一个节点是根节点pFirstNode = pRoot;}else{Convert(pRoot->m_pLeft, pFirstLeft, pLastLeft);// 二叉查找树对应双向有序链表的第一个节点就是左子树转换后双向有序链表的第一个节点pFirstNode = pFirstLeft;// 将根节点和左子树转换后的双向有序链表的最后一个节点连接pRoot->m_pLeft = pLastLeft;pLastLeft->m_pRight = pRoot;}if(pRoot->m_pRight == NULL){// 对应双向有序链表的最后一个节点是根节点pLastNode = pRoot;}else{Convert(pRoot->m_pRight, pFirstRight, pLastRight);// 对应双向有序链表的最后一个节点就是右子树转换后双向有序链表的最后一个节点pLastNode = pLastRight;// 将根节点和右子树转换后的双向有序链表的第一个节点连接pRoot->m_pRight = pFirstRight;pFirstRight->m_pLeft = pRoot;}return;}

6. 求二叉树第K层的节点个数
递归解法:
(1)如果二叉树为空或者k<1返回0
(2)如果二叉树不为空并且k==1,返回1
(3)如果二叉树不为空且k>1,返回左子树中k-1层的节点个数与右子树k-1层节点个数之和
参考代码如下:

int GetNodeNumKthLevel(BinaryTreeNode * pRoot, int k)  {if(pRoot==NULL||k<1)return 0;if(k==1)return 1;int numLeft=GetNodeNumKthLevel(pRoot->m_pLeft,k-1);//右子树中k-1层结点的个数int numRight=GetNodeNumKthLevel(pRoot->m_pRight,k-1);//左子树中k-1层结点的个数return numLeft+numRight;}

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

递归解法:
(1)如果二叉树为空,返回0
(2)如果二叉树不为空且左右子树为空,返回1
(3)如果二叉树不为空,且左右子树不同时为空,返回左子树中叶子节点个数加上右子树中叶子节点个数
参考代码如下:

int GetLeafNodeNum(BinaryTreeNode * pRoot){if(pRoot == NULL)return 0;if(pRoot->m_pLeft == NULL && pRoot->m_pRight == NULL)return 1;int numLeft = GetLeafNodeNum(pRoot->m_pLeft); // 左子树中叶子节点的个数int numRight = GetLeafNodeNum(pRoot->m_pRight); // 右子树中叶子节点的个数return (numLeft + numRight);}

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

不考虑数据内容。结构相同意味着对应的左子树和对应的右子树都结构相同。
递归解法:
(1)如果两棵二叉树都为空,返回真
(2)如果两棵二叉树一棵为空,另一棵不为空,返回假
(3)如果两棵二叉树都不为空,如果对应的左子树和右子树都同构返回真,其他返回假
参考代码如下

bool StructureCmp(BinaryTreeNode * pRoot1, BinaryTreeNode * pRoot2)  {if(pRoot1==NULL&&pRoot2==NULL)return true;if(pRoot1==NULL||pRoot2==NULL) // 有一个为空,一个不为空,返回假 return false; bool resultLeft = StructureCmp(pRoot1->m_pLeft, pRoot2->m_pLeft); // 比较对应左子树    bool resultRight = StructureCmp(pRoot1->m_pRight, pRoot2->m_pRight); // 比较对应右子树   return (resultLeft && resultRight);  }

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

递归解法:
(1)如果二叉树为空,返回真
(2)如果二叉树不为空,如果左子树和右子树都是AVL树并且左子树和右子树高度相差不大于1,返回真,其他返回假
参考代码:

bool IsAVL(BinaryTreeNode * pRoot, int & height){if(pRoot == NULL) // 空树,返回真{height = 0;return true;}int heightLeft;bool resultLeft = IsAVL(pRoot->m_pLeft, heightLeft);int heightRight;bool resultRight = IsAVL(pRoot->m_pRight, heightRight);if(resultLeft && resultRight && abs(heightLeft - heightRight) <= 1) // 左子树和右子树都是AVL,并且高度相差不大于1,返回真{height = max(heightLeft, heightRight) + 1;return true;}else{height = max(heightLeft, heightRight) + 1;return false;}}


10. 求二叉树的镜像

递归解法:
(1)如果二叉树为空,返回空
(2)如果二叉树不为空,求左子树和右子树的镜像,然后交换左子树和右子树
参考代码如下:

BinaryTreeNode * Mirror(BinaryTreeNode * pRoot){if(pRoot == NULL) // 返回NULLreturn NULL;BinaryTreeNode * pLeft = Mirror(pRoot->m_pLeft); // 求左子树镜像BinaryTreeNode * pRight = Mirror(pRoot->m_pRight); // 求右子树镜像        // 交换左子树和右子树pRoot->m_pLeft = pRight;pRoot->m_pRight = pLeft;return pRoot;}

11. 求二叉树中两个节点的最低公共祖先节点
递归解法:
(1)如果两个节点分别在根节点的左子树和右子树,则返回根节点
(2)如果两个节点都在左子树,则递归处理左子树;如果两个节点都在右子树,则递归处理右子树
参考代码如下:


 

bool FindNode(BinaryTreeNode * pRoot, BinaryTreeNode * pNode){if(pRoot == NULL || pNode == NULL)return false;if(pRoot == pNode)return true;bool found = FindNode(pRoot->m_pLeft, pNode);if(!found)found = FindNode(pRoot->m_pRight, pNode);return found;}BinaryTreeNode * GetLastCommonParent(BinaryTreeNode * pRoot,                                      BinaryTreeNode * pNode1,                                      BinaryTreeNode * pNode2){if(FindNode(pRoot->m_pLeft, pNode1)){if(FindNode(pRoot->m_pRight, pNode2))return pRoot;elsereturn GetLastCommonParent(pRoot->m_pLeft, pNode1, pNode2);}else{if(FindNode(pRoot->m_pLeft, pNode2))return pRoot;elsereturn GetLastCommonParent(pRoot->m_pRight, pNode1, pNode2);}}

递归解法效率很低,有很多重复的遍历,下面看一下非递归解法。
非递归解法:
先求从根节点到两个节点的路径,然后再比较对应路径的节点就行,最后一个相同的节点也就是他们在二叉树中的最低公共祖先节点
参考代码如下:

bool GetNodePath(BinaryTreeNode * pRoot, BinaryTreeNode * pNode,                  list<BinaryTreeNode *> & path){if(pRoot == pNode){path.push_back(pRoot);return true;}if(pRoot == NULL)return false;path.push_back(pRoot);bool found = false;found = GetNodePath(pRoot->m_pLeft, pNode, path);if(!found)found = GetNodePath(pRoot->m_pRight, pNode, path);if(!found)path.pop_back();return found;}BinaryTreeNode * GetLastCommonParent(BinaryTreeNode * pRoot, BinaryTreeNode * pNode1, BinaryTreeNode * pNode2){if(pRoot == NULL || pNode1 == NULL || pNode2 == NULL)return NULL;list<BinaryTreeNode*> path1;bool bResult1 = GetNodePath(pRoot, pNode1, path1);list<BinaryTreeNode*> path2;bool bResult2 = GetNodePath(pRoot, pNode2, path2);if(!bResult1 || !bResult2) return NULL;BinaryTreeNode * pLast = 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)pLast = *iter1;elsebreak;iter1++;iter2++;}return pLast;}


在上述算法的基础上稍加变化即可求二叉树中任意两个节点的距离了。
12. 求二叉树中节点的最大距离
即二叉树中相距最远的两个节点之间的距离。
递归解法:
(1)如果二叉树为空,返回0,同时记录左子树和右子树的深度,都为0
(2)如果二叉树不为空,最大距离要么是左子树中的最大距离,要么是右子树中的最大距离,要么是左子树节点中到根节点的最大距离+右子树节点中到根节点的最大距离,同时记录左子树和右子树节点中到根节点的最大距离。

参考代码如下:

int GetMaxDistance(BinaryTreeNode * pRoot, int & maxLeft, int & maxRight){// maxLeft, 左子树中的节点距离根节点的最远距离// maxRight, 右子树中的节点距离根节点的最远距离if(pRoot == NULL){maxLeft = 0;maxRight = 0;return 0;}int maxLL, maxLR, maxRL, maxRR;int maxDistLeft, maxDistRight;if(pRoot->m_pLeft != NULL){maxDistLeft = GetMaxDistance(pRoot->m_pLeft, maxLL, maxLR);maxLeft = max(maxLL, maxLR) + 1;}else{maxDistLeft = 0;maxLeft = 0;}if(pRoot->m_pRight != NULL){maxDistRight = GetMaxDistance(pRoot->m_pRight, maxRL, maxRR);maxRight = max(maxRL, maxRR) + 1;}else{maxDistRight = 0;maxRight = 0;}return max(max(maxDistLeft, maxDistRight), maxLeft+maxRight);}

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

二叉树前序遍历序列中,第一个元素总是树的根节点的值。中序遍历序列中,左子树的节点的值位于根节点的值的左边,右子树的节点的值位
于根节点的值的右边。
递归解法:
(1)如果前序遍历为空或中序遍历为空或节点个数小于等于0,返回NULL。
(2)创建根节点。前序遍历的第一个数据就是根节点的数据,在中序遍历中找到根节点的位置,可分别得知左子树和右子树的前序和中序遍
历序列,重建左右子树。

BinaryTreeNode * RebuildBinaryTree(int* pPreOrder, int* pInOrder, int nodeNum){if(pPreOrder == NULL || pInOrder == NULL || nodeNum <= 0)return NULL;BinaryTreeNode * pRoot = new BinaryTreeNode;// 前序遍历的第一个数据就是根节点数据pRoot->m_nValue = pPreOrder[0];pRoot->m_pLeft = NULL;pRoot->m_pRight = NULL;// 查找根节点在中序遍历中的位置,中序遍历中,根节点左边为左子树,右边为右子树int rootPositionInOrder = -1;for(int i = 0; i < nodeNum; i++)if(pInOrder[i] == pRoot->m_nValue){rootPositionInOrder = i;break;}if(rootPositionInOrder == -1){throw std::exception("Invalid input.");}// 重建左子树int nodeNumLeft = rootPositionInOrder;int * pPreOrderLeft = pPreOrder + 1;int * pInOrderLeft = pInOrder;pRoot->m_pLeft = RebuildBinaryTree(pPreOrderLeft, pInOrderLeft, nodeNumLeft);// 重建右子树int nodeNumRight = nodeNum - nodeNumLeft - 1;int * pPreOrderRight = pPreOrder + 1 + nodeNumLeft;int * pInOrderRight = pInOrder + nodeNumLeft + 1;pRoot->m_pRight = RebuildBinaryTree(pPreOrderRight, pInOrderRight, nodeNumRight);return pRoot;}

同样,有中序遍历序列和后序遍历序列,类似的方法可重建二叉树,但前序遍历序列和后序遍历序列不同恢复一棵二叉树,证明略。
14.判断二叉树是不是完全二叉树
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全
二叉树。
有如下算法,按层次(从上到下,从左到右)遍历二叉树,当遇到一个节点的左子树为空时,则该节点右子树必须为空,且后面遍历的节点左
右子树都必须为空,否则不是完全二叉树。

bool IsCompleteBinaryTree(BinaryTreeNode * pRoot){if(pRoot == NULL)return false;queue<BinaryTreeNode *> q;q.push(pRoot);bool mustHaveNoChild = false;bool result = true;while(!q.empty()){BinaryTreeNode * pNode = q.front();q.pop();if(mustHaveNoChild) // 已经出现了有空子树的节点了,后面出现的必须为叶节点(左右子树都为空){if(pNode->m_pLeft != NULL || pNode->m_pRight != NULL){result = false;break;}}else{if(pNode->m_pLeft != NULL && pNode->m_pRight != NULL){q.push(pNode->m_pLeft);q.push(pNode->m_pRight);}else if(pNode->m_pLeft != NULL && pNode->m_pRight == NULL){mustHaveNoChild = true;q.push(pNode->m_pLeft);}else if(pNode->m_pLeft == NULL && pNode->m_pRight != NULL){result = false;break;}else{mustHaveNoChild = true;}}}return result;}



 

 

 

 

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 穿越火线与主机连接不稳定怎么办 word被锁定无法编辑怎么办 平衡车系统乱了怎么办 监控老是滴滴的响怎么办 磁盘已满 文件未保存怎么办 cocos只有代码没有项目怎么办 电脑系统管理员密码忘记了怎么办 魔兽小队不显示职业颜色怎么办 魔兽多余的橙装怎么办? f117-f6不读硬盘怎么办 中飞院飞行学生停飞了怎么办 军人对你敬礼时怎么办 小孩抱着就睡放下就醒怎么办 着火了怎么办 我的世界 生存战争2感冒了怎么办 生存战争2吐了怎么办 我的世界hqm重置怎么办 不小心打了110怎么办 我的世界皮肤有黑影怎么办 我的世界字体变大了怎么办 生锈的铁钉扎了怎么办 每天晚上窗纱上老有蝙蝠倒挂怎么办 我的世界没有痒怎么办 七日杀被ban了怎么办 吕框箱子上保护摸撕不掉怎么办 我的世界开光影卡怎么办 我的世界买不了怎么办 我的世界延迟高怎么办 我的世界过于昂贵怎么办 白色麻布染上别的颜色怎么办 印度老山檀香开裂了怎么办 专升本没过线怎么办 西安公租房小孩上学怎么办 全民k歌直播没人怎么办 在全民直播没人看怎么办 皮肤又黄又粗怎么办 被强制消费后应怎么办? 当保安不发工资怎么办? 辅警改革流管员怎么办 退伍证上照片毁了怎么办 辅警年龄大了怎么办