【代码 C/C++】二叉树层次建树

正式开始打代码前的一些小知识

辅助队列

lchild=B,A,rcild=C
lchild=NULL,C,rcild=NULL
lchild=NULL,B,rcild=NULL

辅助队列如下

A
B
C
phead
Ptail

此时判断得知A的左右子树已满,因此将A出队,辅助队列如下

B
C
phead
Ptail

继续插入元素D时,查询辅助队列得知队首为B,检查B得知左子树为NULL
,将D作为B的左子树插入。辅助队列如下

B
C
F
phead
Ptail

calloc和malloc

calloc同样是申请空间的函数,和malloc的区别在于,calloc申请的空间内容会全部赋0。
对于二叉树的新建,每插入一个结点就需要将左右孩子的值赋为NULL,而使用calloc时就可以省略这一步。
calloc()的函数声明

void *calloc(size_t nitems, size_t size)
//nitems -- 要被分配的元素个数。
//size --*斜体样式* 元素的大小。

代码部分

结构体

typedef char ElemType;
//二叉树的数据结构
typedef struct BiTNode{
	ElemType data;
	struct BiTNode* lchild;
	struct BiTNode* rchild;
}BiTNode, *BiTree;

//用于辅助队列标记
typedef struct tag {
	BiTree p;
	struct tag* pnext;
}tag_t,*ptag_t;

二叉树建树

我调试了一下,简单的输入没有什么问题(看完王道的课自己试着写的)

//新建树
	BiTree tree = NULL;//树根
	//新结点
	BiTree pnew;
	//结点数据
	ElemType newdata;
	ptag_t phead = NULL, ptail = NULL, pcur;//phead指向队头,ptail指向队尾,pcur指向当前需要操作的结点
	ptag_t listpnew;//listpnew指向新入队的结点
	//输入一串字符,以回车作为结束
	while (scanf("%c", &newdata) != EOF)
	{
		//回车,跳出循环
		if (newdata == '\n')
		{
			break;
		}
		//新的结点pnew
		pnew = (BiTNode*)calloc(1, sizeof(BiTNode));
		pnew->data = newdata;
		//新的入队结点listpnew
		listpnew = (ptag_t)calloc(1, sizeof(tag_t));
		listpnew->p = pnew;
		//树为空
		if (tree==NULL)
		{
			phead = listpnew;
			ptail = listpnew;
			//pcur = listpnew;
			tree = pnew;
			continue;
		}
		else
		{
			ptail->pnext = listpnew;
			ptail = ptail->pnext;
			//pcur = ptail;
		}
		//首先确认队头结点的左右孩子是否已满,已满则出队
		if (phead->p->lchild != NULL && phead->p->rchild != NULL)
		{
			phead = phead->pnext;
		}
		//左孩子为空,则先将新结点放入队头结点的左孩子
		if (phead->p->lchild == NULL)
		{
			phead->p->lchild = pnew;
		}
		//否则放入右孩子
		else if (phead->p->rchild == NULL)
		{
			phead->p->rchild = pnew;
		}
	}

与王道的代码做对比

//读取字符串abcdefghij,然后层次建树建立一颗二叉树,然后前序遍历输出abdhiejcfg,注意不要打印前序遍历几个汉字
//思路:层次遍历创建二叉树,用队列记录节点(该节点是否有左右孩子)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>
//二叉数的结构体,用链表实现
typedef  char ElemType;
typedef struct BiTNode
{
	ElemType data;
	struct BiTNode* lchild, * rchild;//左右孩子指针
}BiTNode, * BiTree;//二叉树的节点,二叉树本树

//记录节点的链队,若节点的左右孩子不为空,则出队
typedef struct label
{
	BiTree p;//树的某一个结点的地址值
	struct label* pnext;
}label, * Label;//链队节点,链队

int main()
{
	BiTree pnew;//树的新节点
	BiTree tree = NULL;//树根为空

	//层次遍历的辅助队列
	Label phead = NULL, ptail = NULL;//phead就是队列头,ptail就是队列尾
	Label listpnew;//链队的新节点
	Label pcur = NULL;//listpnew是队列的新节点 pcur是树的当前节点

	ElemType c;
	while (scanf("%c", &c) != EOF)
	{
		if (c == '\n')
		{
			break;
		}
		pnew = (BiTree)calloc(1, sizeof(BiTNode));//calloc申请空间并对空间进行初始化
		pnew->data = c;//数据放进去

		listpnew = (Label)calloc(1, sizeof(Label));//给队列结点申请空间
		listpnew->p = pnew;//赋值

		if (NULL == tree)//树根为空
		{
			tree = pnew;//树的根
			phead = listpnew;//队列头
			ptail = listpnew;//队列尾
			pcur = listpnew;
			continue;//跳出本次循环
		}
		else {
			ptail->pnext = listpnew;//新结点放入链表,通过尾插法//尾插法入队
			ptail = listpnew;//ptail指向队列尾部
		}

		//pcur始终指向要插入的结点的位置
		if (NULL == pcur->p->lchild)//如何把新结点放入树
		{
			pcur->p->lchild = pnew;//把新结点放到要插入结点的左边
		}
		else if (NULL == pcur->p->rchild)
		{
			pcur->p->rchild = pnew;//把新结点放到要插入结点的右边
			pcur = pcur->pnext;//左右都放了结点后,pcur指向队列的下一个
		}
	}
}

看了一下王道的代码,和我写的区别在于pcur:
可能是听课的时候没太明白pcur具体是什么,我写的时候将队列中队头左右子树已满的元素直接出队,也就是phead指向下一个结点;
王道的代码则是phead一直指向根结点,用pcur来表示当前左右子树未满还可以插入的结点。
看我的代码我是将pcur理解成了新插入的结点,所以没用到pcur。
思考了一下还是有pcur比较合理并且完整,可以保留下完整的插入结点,如果之后还需要用到所有结点的话会更方便。

打代码时发现的关于BiTNode和BiTree一些注意点(其实很简单但好久才想明白

我写了这样一句代码

pnew = (BiTNode*)calloc(1, sizeof(BiTree));

是一句错的代码,正确写法应该是

pnew = (BiTNode*)calloc(1, sizeof(BiTNode));

之前写类似空间申请时一般都会写对,但一直没仔细注意过这个点,在写二叉树层次建树这部分时不小心就写错了,后来运行代码一直不对,还以为是我对calloc()函数理解错了

我在这段纠结的时间完全确认了一下一点(其实是基础但是我一直没放心上):
对于

typedef struct BiTNode{
	ElemType data;
	struct BiTNode* lchild;
	struct BiTNode* rchild;
}BiTNode, *BiTree;
sizeof(BiTNode) = 12
sizeof(BiTNode*) = 4
sizeof(BiTree) = 4

原因在于BiTNode*和BiTree只是指针!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值