二叉树的最大宽度

题目:

给定一个二叉树,计算二叉树的最大宽度(二叉树的最大宽度是指二叉树所有层次中结点个数的最大值)。

分析:想到求一个二叉树的最大宽度,不难想到层序遍历,即采用入队和出队的方式来寻找二叉树的最大宽度,ok,话不多说,直接开整~


  • 首先定义一个树的结构体

//树的结构体 
typedef struct BinNode{
	char data;
	struct BinNode *lchild,*rchild;
}BinNode,*BinTree;
typedef BinTree ElemType;//把队列的元素定义为树

lchild和rchild分别代表树的左孩子和右孩子,BinTree为结构体指针;typedef BinTree ElemType;主要是我们需要把树的结点放到队列里面,,所以把队列的元素定义为树~ 

  • 利用先序遍历来创建一个二叉树 

void CreateBinTree(BinTree *T)
{
	char ch;
	if((ch=getchar())=='#')	*T=NULL;
	else
	{
		(*T)=(BinTree)malloc(sizeof(BinNode));
		(*T)->data=ch;
		CreateBinTree((BinTree *)&((*T)->lchild));//强制类型转换 (BinTree *)
		CreateBinTree((BinTree *)&((*T)->rchild));
	}
	printf("创建成功!\n");
}

 由于T本身就是指针类型,再传入一个指针即(**T),所以对T进行解引用操作;递归调用函数时(BinTree *)表示强制类型转换。用'#'表示空

 此时树的操作我们已经差不多搞完了,现在要考虑一下什么是树的层次遍历。

由于层次遍历我们需要用到队列,所以我们先写出关于队列的一系列操作~

  • 队列的操作

//队
typedef struct Queue{
	ElemType data[MAXSIZE];
	int front,rear;
}Queue,*PtrQueue;

//初始化队列
void InitQueue(PtrQueue Q)
{
	Q->front=Q->rear=0;
	memset(Q->data,0,sizeof(ElemType)*MAXSIZE);
}
//判满
int IsFull(PtrQueue Q)
{
    if ((Q->rear+1) % MAXSIZE == Q->front)  return 1;
    return 0;
}
//判空
int IsEmpty(PtrQueue Q)
{
    if (Q->rear == Q->front)    return 1;
    return 0;	
}
//入队
void EnQueue(PtrQueue Q,ElemType *e)
{
	if(!Q || !e)	return;//队为空或者树结点为空
	if(!IsFull(Q)) 
	{ 
		memcpy(&Q->data[Q->rear],e,sizeof(ElemType));
		Q->rear=(Q->rear+1)%MAXSIZE;
	}
}
//出队
void DeQueue(PtrQueue Q,ElemType *e)
{
	if(!Q)	return;
	if(!IsEmpty(Q))
	{
		memcpy(e,&Q->data[Q->front],sizeof(ElemType));
		Q->front=(Q->front+1)%MAXSIZE;
	}
}

队列的顺序存储就不过多介绍了,这里简单介绍一下memcpy函数和memset函数,两个函数都包含在<string.h>。

 memset():C 库函数 void *memset(void *str, int c, size_t n) 复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。

  • str -- 指向要填充的内存块。
  • c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。
  • n -- 要被设置为该值的字符数。

memcpy():C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字节到存储区 str1。

  • str1 -- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
  • str2 -- 指向要复制的数据源,类型强制转换为 void* 指针。
  • n -- 要被复制的字节数。
  •  层序遍历

 分析:层序遍历即利用队列先进先出的特点,现将根节点入队,若队列不空则将根节点出队,然后将根节点的左右孩子入队再判断队列空不空,若不空则出队一个元素,入队其左右孩子,依次循环~

如图所示,左边一颗二叉树,AB表示已经出队,队列里面元素为CDE

//层次遍历
void LevelOrderBinTree(BinTree T)
{
	Queue Q;
	ElemType e=T;
	InitQueue(&Q);
	EnQueue(&Q,&e);
	while(!IsEmpty(&Q))
	{
		DeQueue(&Q,&e);
		visit(e);
		if(e->lchild)	EnQueue(&Q,&e->lchild);
		if(e->rchild)	EnQueue(&Q,&e->rchild);
	}
}

知道了这个原理我们就可以求一个二叉树的最大宽度了~

  • 二叉宽度 

//宽度
int WidthBinTree(BinTree T)
{
	if(!T)	return 0;//空树 
	Queue Q;
	ElemType e=T;
	int temp=0,last=0,max=0;//last为一层中最后一个结点在队列的位置, 
	InitQueue(&Q);
	EnQueue(&Q,&e);
	while(Q.front <= last){//每出队一个元素front+1 
        DeQueue(&Q, &e);
        temp++;//temp为树每层的结点个数 
        if (e->lchild != NULL)//每次入队rear+1 
            EnQueue(&Q, &e->lchild);
        if (e->rchild != NULL)
            EnQueue(&Q, &e->rchild);
        if (Q.front > last){//每一层结束后更新 
            last = Q.rear-1;
            max = max > temp ? max : temp;
            temp = 0;
        }
    }
    return max;
}

 按照层序遍历的思路,我们将根节点入队,然后出队,temp表示二叉树每层的结点,每次出队一个元素temp++,然后把根节点的左右孩子入队,判断front指针是否大于last(每层结点最后元素的位置),即判断这一层的元素是否全部出队(全部出队则front与上一次的rear相等,即大于last(rear-1)),如果全部出队则更新last,max(最大宽度)和temp,最后递归所有元素然后返回最大宽度max。

  • 完整代码 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define MAXSIZE 100
//树的结构体 
typedef struct BinNode{
	char data;
	struct BinNode *lchild,*rchild;
}BinNode,*BinTree;
typedef BinTree ElemType;
//队
typedef struct Queue{
	ElemType data[MAXSIZE];
	int front,rear;
}Queue,*PtrQueue;

//初始化队列
void InitQueue(PtrQueue Q)
{
	Q->front=Q->rear=0;
	memset(Q->data,0,sizeof(ElemType)*MAXSIZE);
}
//判满
int IsFull(PtrQueue Q)
{
    if ((Q->rear+1) % MAXSIZE == Q->front)  return 1;
    return 0;
}
//判空
int IsEmpty(PtrQueue Q)
{
    if (Q->rear == Q->front)    return 1;
    return 0;	
}
//入队
void EnQueue(PtrQueue Q,ElemType *e)
{
	if(!Q || !e)	return;//队为空或者树结点为空
	if(!IsFull(Q)) 
	{ 
		memcpy(&Q->data[Q->rear],e,sizeof(ElemType));
		Q->rear=(Q->rear+1)%MAXSIZE;
	}
}
//出队
void DeQueue(PtrQueue Q,ElemType *e)
{
	if(!Q)	return;
	if(!IsEmpty(Q))
	{
		memcpy(e,&Q->data[Q->front],sizeof(ElemType));
		Q->front=(Q->front+1)%MAXSIZE;
	}
}
//先序创建树
void CreateBinTree(BinTree *T)
{
	char ch;
	if((ch=getchar())=='#')	*T=NULL;
	else
	{
		(*T)=(BinTree)malloc(sizeof(BinNode));
		(*T)->data=ch;
		CreateBinTree((BinTree *)&((*T)->lchild));//强制类型转换 (BinTree *)
		CreateBinTree((BinTree *)&((*T)->rchild));
	}
	printf("创建成功!\n");
}
void visit(BinTree T)
{
	printf("%c",T->data);
}
//层次遍历
void LevelOrderBinTree(BinTree T)
{
	Queue Q;
	ElemType e=T;
	InitQueue(&Q);
	EnQueue(&Q,&e);
	while(!IsEmpty(&Q))
	{
		DeQueue(&Q,&e);
		visit(e);
		if(e->lchild)	EnQueue(&Q,&e->lchild);
		if(e->rchild)	EnQueue(&Q,&e->rchild);
	}
}
//宽度
int WidthBinTree(BinTree T)
{
	if(!T)	return 0;//空树 
	Queue Q;
	ElemType e=T;
	int temp=0,last=0,max=0;//last为一层中最后一个结点在队列的位置, 
	InitQueue(&Q);
	EnQueue(&Q,&e);
	while(Q.front <= last){//每出队一个元素front+1 
        DeQueue(&Q, &e);
        temp++;//temp为树每层的结点个数 
        if (e->lchild != NULL)//每次入队rear+1 
            EnQueue(&Q, &e->lchild);
        if (e->rchild != NULL)
            EnQueue(&Q, &e->rchild);
        if (Q.front > last){//每一层结束后更新 
            last = Q.rear-1;
            max = max > temp ? max : temp;
            temp = 0;
        }
    }
    return max;
}
int main(){
    BinTree T = NULL;
    int width;
    printf("请构造一棵二叉树(#表示空):\n");
    CreateBinTree(&T);
    printf("层次遍历序列:\n");
    LevelOrderBinTree(T);
    putchar('\n');
	width = WidthBinTree(T);
    printf("树的最大宽度为:%d\n", width);
    return 0;
}

  • 15
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

m0_嘉木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值