前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【数据结构】二叉树

【数据结构】二叉树

作者头像
YoungMLet
发布2024-03-01 10:29:35
800
发布2024-03-01 10:29:35
举报
文章被收录于专栏:C++/Linux
二叉树

一、二叉树的链式结构实现

二叉树链式结构的简单实现: 此处为了快速创建一棵二叉树,只是简单创建每一个节点然后把它们连接起来;

示例代码:

代码语言:javascript
复制
		typedef int BTDataType;
		
		//节点的结构体
		typedef struct BinaryTreeNode
		{
			struct BinaryTreeNode* left;
			struct BinaryTreeNode* right;
			BTDataType data;
		}BTNode;
		
		
		//创建新的节点
		BTNode* BuyNode(BTDataType x)
		{
			BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
			assert(newnode);
		
			newnode->data = x;
			newnode->left = NULL;
			newnode->right = NULL;
		
			return newnode;
		}
		
		//创建二叉树
		BTNode* CreatBinaryTree()
		{
			BTNode* node1 = BuyNode(1);
			BTNode* node2 = BuyNode(2);
			BTNode* node3 = BuyNode(4);
			BTNode* node4 = BuyNode(3);
			BTNode* node5 = BuyNode(5);
			BTNode* node6 = BuyNode(6);
		
			node1->left = node2;
			node1->right = node3;
			node2->left = node4;
			
			node3->left = node5;
			node3->right = node6;
		
			return node1;
		}

以上代码创建的二叉树如图:

二、二叉树的遍历

所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。

1. 二叉树的前序遍历

二叉树的前序遍历是先访问根节点,再访问左子树,最后访问右子树,即按照根 - 左子树 - 右子树的顺序遍历;示例代码:

代码语言:javascript
复制
		//前序遍历
		void PrevOrder(BTNode* root)
		{
			if (root == NULL)
			{
				printf("NULL ");
				return ;
			}
		
			printf("%d ", root->data);
			PrevOrder(root->left);
			PrevOrder(root->right);
		}

代码通过子问题,先打印根的数据,再将当前根的左子树进行递归,最后对右子树递归,结束条件为空,即完成了 根 - 左子树 - 右子树 的顺序访问,若以上面我们创建的二叉树为例,如图为运行结果,假设 N 为空:

遍历的过程我们可以分解为如下,假设 N 为空;

以下是以 1 为根的分解,左子树和右子树如下,

再往下分解如下:

所以遍历的顺序图如下,蓝色数字为访问节点的顺序:

先访问 1 的根节点,然后访问左子树,递归图解:

最后访问右子树,递归图解:

2. 二叉树的中序遍历

二叉树的中序遍历,与前序的区别是,中序遍历先访问左子树,再访问根,最后访问右子树,即按照左子树 - 根 - 右子树的顺序遍历;示例代码:

代码语言:javascript
复制
		//中序遍历
		void InOrder(BTNode* root)
		{
			if (root == NULL)
			{
				printf("N ");
				return;
			}
		
			InOrder(root->left);
			printf("%d ", root->data);
			InOrder(root->right);
		}

若以上面我们创建的二叉树为例,以下就是中序遍历的结果:

3. 二叉树的后序遍历

二叉树的后序遍历,与前序和中序相似,后序是先访问左子树,再访问右子树,最后访问根;按照左子树 - 右子树 - 根的顺序遍历;示例代码:

代码语言:javascript
复制
		//后序遍历
		void PostOrder(BTNode* root)
		{
			if (root == NULL)
			{
				printf("N ");
				return;
			}
		
			PostOrder(root->left);
			PostOrder(root->right);
			printf("%d ", root->data);
		}

若以上面我们创建的二叉树为例,以下就是后序遍历的结果:

4. 二叉树的层序遍历

层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

按如图顺序遍历为层序遍历:

层序遍历的思路是用队列实现,每打印出一个根的数据,就将这个根的左节点和右节点入队,如下图,使用上面我们创建的二叉树,再创建一个队列 q ,队列中存的是每个树的节点:

如果队列为空,根就进队列:

然后出队列,出队列后,根2和根4入队:

2出队,3和NULL入队:

最后层序遍历的结果如下:

代码示例:

我们使用以前实现的队列,将队列中每个节点的指针类型改为 BTNode* :

代码语言:javascript
复制
		typedef struct BinaryTreeNode* QDataType;
		
		//每个节点的结构体
		typedef struct QueueNode
		{
			struct QueueNode* next;
			QDataType data;
		}QNode;
		
		//队列的结构体
		typedef struct Queue
		{
			QNode* phead;
			QNode* ptail;
			int size;
		}Queue;

层序遍历的示例代码:

代码语言:javascript
复制
		//层序遍历
		void LevelOrder(BTNode* root)
		{
			//用队列实现层序遍历
			Queue q;
			QueueInit(&q);
		
			//节点不为空,就入队
			if(root)
				QueuePush(&q, root);
		
			//每次出队之前,将队头的数据记录下来,然后出队,打印队头的值,再将出队的根的左右子树入队
			while (!QIsEmpty(&q))
			{
				BTNode* front = QueueFront(&q);
		
				QueuePop(&q);
		
				printf("%d ", front->data);
		
				if(front->left)
					QueuePush(&q, front->left);
		
				if(front->right)
					QueuePush(&q, front->right);
			}
		
			printf("\n");
		
			QueueDestroy(&q);
		}

假设不打印空,运行的结果如下:

三、求二叉树的常见问题

1. 求二叉树节点个数

求二叉树节点个数化成子问题,左子树的节点个数加上右子树的节点个数加上自身节点;结束条件为空:

代码语言:javascript
复制
		//二叉树节点个数
		int BTreeSize(BTNode* root)
		{
			if (root == NULL)
				return 0;
		
			return BTreeSize(root->left) + BTreeSize(root->right) + 1;
		}

若以上面创建的树为例子,树的节点个数以及运行结果如下:

2. 求二叉树叶子节点个数

求二叉树叶子节点个数化成子问题,将根的左子树的叶子数加上右子树的叶子数,结束条件为空或者根的左子树和右子树都为空,就是叶子,返回1;

代码语言:javascript
复制
		//求叶子节点的个数
		int BTreeLeafSize(BTNode* root)
		{
			if (root == NULL)
				return 0;
		
			if (root->left == NULL && root->right == NULL)
				return 1;
		
			return BTreeLeafSize(root->left) + BTreeLeafSize(root->right);
		}

运行结果如下:

3. 求二叉树的高度

求二叉树的高度化成子问题,取根的左子树和右子树中高度较高的那个,返回的是较高的那个再加一;结束条件为空;注意这里需要记录左子树和右子树的高度,不记录数据的话每次递归完会丢失数据,还要重新找数据,时间复杂度会翻呈指数形式的倍率;

代码语言:javascript
复制
		int BTreeHeight(BTNode* root)
		{
			if (root == NULL)
				return 0;
		
			int leftHeight = BTreeHeight(root->left);
			int rightHeight = BTreeHeight(root->right);
		
			return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
		}

运行结果如下:

4. 求二叉树第k层结点个数

求二叉树第k层结点个数化成子问题,求当前根的左子树的 k - 1 层加上右子树的 k - 1 层的节点个数;结束条件有两个,如果为空,返回0;如果 k == 1,表示就是第 k 层,返回1;

代码语言:javascript
复制
		int BTreeLevelKSize(BTNode* root, int k)
		{
			assert(k > 0);
		
			if (root == NULL)
				return 0;
		
			if (k == 1)
				return 1;
		
			return BTreeLevelKSize(root->left, k - 1) + BTreeLevelKSize(root->right, k - 1);
		}

假设求第一层的节点,运行结果如下:

5. 二叉树查找值为x的节点

二叉树查找值为x的节点化成子问题,先判断当前的根是否是值为 x 的节点,若不是,就找在左子树中去找,在左子树中找不到,再去右子树找;结束条件为空或者找到值为x的节点;

代码语言:javascript
复制
		BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
		{
			if (root == NULL)
				return NULL;
		
			if (root->data == x)
				return root;
		
			BTNode* r1 = BinaryTreeFind(root->left, x);
			if (r1)
				return r1;
			BTNode* r2 = BinaryTreeFind(root->right, x);
			if (r2)
				return r2;
		
			return NULL;
		}

假设查找值为 6 的节点,运行结果如下:

6. 判断二叉树是否是完全二叉树

判断二叉树是否是完全二叉树,思路是用层序遍历的思维,完全二叉树是层序遍历连续的结果,如果遇到空后,队列中的数据还有有效的数据,说明不是完全二叉树;如果是完全二叉树,队列中应该全部是空;

代码语言:javascript
复制
		//判断二叉树是否是完全二叉树
		bool BTreeComplete(BTNode* root)
		{
			Queue q;
			QueueInit(&q);
		
			if (root)
				QueuePush(&q, root);
		
			//出数据,遇到空就停止出
			while (!QIsEmpty(&q))
			{
				BTNode* front = QueueFront(&q);
				QueuePop(&q);
		
				if (front == NULL)
					break;
		
				QueuePush(&q, front->left);
				QueuePush(&q, front->right);
			}
		
			//判断队列里面是否还有数据,如果还有数据说明不是完全二叉树
			//完全二叉树是层序遍历连续的结果
			while (!QIsEmpty(&q))
			{
				BTNode* front = QueueFront(&q);
				QueuePop(&q);
		
				if (front)
				{
					QueueDestroy(&q);
					return false;
				}
			}
			QueueDestroy(&q);
			return true;
		}

假设false为0,true为1,运行结果为:

7. 给定前序遍历创建一颗二叉树

给定前序遍历创建一颗二叉树(假设0为空),思路是用前序遍历的思路,先创建根,再创建左子树,最后创建右子树;假设遍历的前序中 0 为空,参考代码如下:

代码语言:javascript
复制
		//给定前序遍历,创建一棵树
		BTNode* CreatTree(BTDataType* a, int* pi)
		{
		
			if (a[*pi] == 0)
			{
				(*pi)++;
				return NULL;
			}
		
			BTNode* root = BuyNode(a[(*pi)++]);
			root->left = CreatTree(a, pi);
			root->right = CreatTree(a, pi);
		
			//返回根
			return root;
		}
		
		
		int main()
		{
			int arr[] = { 1,2,3,0,0,0,4,5,0,0,6,0,0 };
			int i = 0;
			BTNode* root = CreatTree(arr, &i);
			PrevOrder(root);
		}

8. 二叉树销毁

二叉树销毁是按照后序的顺序,先销毁左子树,再销毁右子树,最后销毁根;

代码语言:javascript
复制
		// 二叉树销毁
		void BTreeDestory(BTNode* root)
		{
			if (root == NULL)
				return;
		
			BTreeDestory(root->left);
			BTreeDestory(root->right);
			free(root);
		}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-01-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 二叉树
  • 一、二叉树的链式结构实现
  • 二、二叉树的遍历
    • 1. 二叉树的前序遍历
      • 2. 二叉树的中序遍历
        • 3. 二叉树的后序遍历
          • 4. 二叉树的层序遍历
          • 三、求二叉树的常见问题
            • 1. 求二叉树节点个数
              • 2. 求二叉树叶子节点个数
                • 3. 求二叉树的高度
                  • 4. 求二叉树第k层结点个数
                    • 5. 二叉树查找值为x的节点
                      • 6. 判断二叉树是否是完全二叉树
                        • 7. 给定前序遍历创建一颗二叉树
                          • 8. 二叉树销毁
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档