《数据结构》上机实验(第七章) —树与二叉树Ⅰ

在这里插入图片描述

1. 求二叉树中距离根结点最近/最远的叶子结点。

求距离根结点最近的叶子结点

  • 算法思想:采用基于先序遍历的递归方法,用min记录结点层次(初始值取一个大整数),表示当前访问结点的层次。先判断根结点,若为叶子结点,其层次l小于min,置min=l,用x记录其结点值;再到左子树中查找并比较;最后到右子树中查找并比较。
void MinLeaf(BiTNode* b, int l, int &min, char& x)
{
//l的初值为1,min取最大整数,x为所求的叶子结点
	if (b == NULL) return;
	else
	{
		if (b->lchild == NULL && b->rchild == NULL)
		{
			if (l < min)
			{
				min = l;
				x = b->data;
			}
		}
		MinLeaf(b->lchild, l + 1, min, x);
		MinLeaf(b->rchild, l + 1, min, x);
	}
}

求距离根结点最远的叶子结点

//l的初值为1,max取最小整数,y为所求的叶子结点
void MaxLeaf(BiTNode* b, int l, int& max, char& y)
{
	if (b == NULL) return;
	else
	{
		if (b->lchild == NULL && b->rchild == NULL)
		{
			if (l > max)
			{
				max = l;
				y = b->data;
			}
		}
		MaxLeaf(b->lchild, l + 1, max, y);
		MaxLeaf(b->rchild, l + 1, max, y);
	}
}

运行结果

在这里插入图片描述

2. 假设二叉树采用二叉链存储结构,求二叉树b中结点值为x的结点的层次(或者深度)。

  • 算法思想:Level(BiTNode *b,ElemType x,int h),其返回值为在二叉树b中查找结点值为x的结点所在的层次,返回0表示未找到。如果b为空树,返回0;如果当前根结点的结点值为x,则返回h;否则在左子树中査找(层次h需要增1),若在左子树中未找到,再在右子树中查找(层次h需要增1)。
int Level(BiTNode* b, ElemType x, int h) //h置初值1
{
	int lev;
	if (b == NULL) return 0;
	else if (b->data == x) return h;
	else
	{
		lev = Level(b->lchild, x, h + 1); //在左子树中查找
		if (lev != 0) return lev; //在左子树中找到了,返回1
		else lev = Level(b->rchild, x, h + 1); //在左子树中未找到,再在右子树中查找
	}
}

3. 输出从根结点到每个叶子结点的路径逆序列

  • 算法思想:利用后序遍历非递归算法的特点,将其中的访问结点改为判断该结点是否为叶子结点,若是,输出栈中的所有结点值。
void AllPath(BiTNode* b)
{
	BiTNode* p, * r;
	bool flag;
	SqStack* st; //定义一个顺序栈指针st
	InitStack(st); //初始化栈st
	p = b;
	do {
		while (p != NULL) //扫描结点p的所有左下结点并进栈
		{
			Push(st, p); //结点p进栈
			p = p->lchild; //移到到左孩子
		}
		r = NULL; //r指向刚访问的结点,初始时为空
		flag = true; //flag为真表示正在处理栈顶结点
		while (!EmptyStack(st) && flag)
		{
			GetTop(st, p); //取出当前的栈顶结点p
			if (p->rchild == r) //若结点p的右孩子为空或者为刚刚访问过的结点
			{
				if (p->lchild == NULL && p->rchild == NULL)
				{
					for (int i = st->top; i > 0; i--)
						printf("%c -> ", st->data[i]->data);
					printf("%c\n", st->data[0]->data);
				}
				Pop(st, p);
				r = p; //r指向刚访问过的结点
			}
			else
			{
				p = p->rchild; //转向处理其右子树
				flag = false; //表示当前不是处理栈顶结点
			}
		}
	} while (!EmptyStack(st)); //栈不空循环
	printf("\n");
	DestroyStack(st); //销毁栈
}

运行结果

在这里插入图片描述

4. 输出每个叶子结点到根结点的逆路径

  • 算法思想:采用基于先序遍历递归算法,用path数组存放查找的路径,pathlen存放路径长度,当找到叶子结点b时,由于b叶子结点尚未添加到path中,因此在输出路径时还需输出b->data值;若b不为叶子结点,将b->data放入path中,然后在左、右子树中递归查找。
void AllPath(BiTNode* b, ElemType path[], int pathlen)
{
	if (b == NULL) return;
	else
	{
		if (b->lchild == NULL && b->rchild == NULL) //b为叶子结点
		{
			printf("%c到根结点逆路径:%c ", b->data, b->data);
			for (int i = pathlen - 1; i >= 0; i--) printf("%c ", path[i]);
			printf("\n");
		}
		else
		{
			path[pathlen] = b->data; //将当前结点放入路径中
			pathlen++; //路径长度+1
			AllPath(b->lchild, path, pathlen); //递归扫描左子树
			AllPath(b->rchild, path, pathlen); //递归扫描右子树
			pathlen--; //恢复环境
		}
	}
}

运行结果

在这里插入图片描述

5. 输出该二叉树中第一条最长的/最短的路径长度,并输出此路径上各结点的值。

第一条最长的路径

  • 算法思想:采用基于先序递归算法的思路。用path[ ]保存扫描到当前结点的路径,pathlen保存扫描到当前结点的路径长度,longpath保存最长的路径,longpathlen保存最长路径长度。当b为空时,表示当前扫描的一个分支已扫描完毕,将pathlen与longpathlen进行比较,将较长的路径及路径长度分别保存在longth[ ]和longpathlen中。
void LongPath(BiTNode* b, ElemType path[], int pathlen, ElemType longpath[], int& longpathlen)
{
	//pathlen的初值为0,longpathlen的初值取最小整数
	if (b == NULL)
	{
		if (pathlen > longpathlen) //若当前路径更长,将路径保存在longpath中
		{
			for (int i = pathlen - 1; i >= 0; i--) longpath[i] = path[i];
			longpathlen = pathlen;
		}
	}
	else
	{
		path[pathlen] = b->data; //将当前结点放入路径中
		pathlen++; //路径长度+1
		LongPath(b->lchild, path, pathlen, longpath, longpathlen); //递归扫描左子树
		LongPath(b->rchild, path, pathlen, longpath, longpathlen); //递归扫描右子树
	}
}

第一条最短的路径

void ShortPath(BiTNode* b, ElemType path[], int pathlen, ElemType shortpath[], int& shortpathlen)
{
	//pathlen的初值为0,shortpathlen的初值取最大整数
	if (b == NULL)
	{
		if (pathlen < shortpathlen) //若当前路径更短,将路径保存在shortpath中
		{
			for (int i = pathlen - 1; i >= 0; i--) shortpath[i] = path[i];
			shortpathlen = pathlen;
		}
	}
	else
	{
		path[pathlen] = b->data; //将当前结点放入路径中
		pathlen++; //路径长度+1
		ShortPath(b->lchild, path, pathlen, shortpath, shortpathlen); //递归扫描左子树
		ShortPath(b->rchild, path, pathlen, shortpath, shortpathlen); //递归扫描右子树
	}
}

运行结果

  • 所谓最小枝长是指根结点到最近叶子结点的路径长度;最大枝长是指根结点到最远叶子结点的路径长度。
    在这里插入图片描述

6. 求二叉树b的最小/最大枝长。所谓最小/最大枝长是指根结点到最近/最远叶子结点的路径长度。

二叉树b的最小枝长

  • 算法思想:采用基于先序遍历的递归方法,先判断根结点不空,再求出左、右子树的最小枝长min1和min2,并返回MIN(min1,min2)+1。
int MinBranch(BiTNode* b) //求二叉树的最小枝长
{
	int min1, min2, min;
	if (b == NULL) return 0;
	else
	{
		min1 = MinBranch(b->lchild);
		min2 = MinBranch(b->rchild);
		if (min1 < min2) min = min1 + 1;
		else min = min2 + 1;
		return min;
	}
}

二叉树b的最大枝长

int MaxBranch(BiTNode* b) //求二叉树的求最大枝长
{
	int max1, max2, max;
	if (b == NULL) return 0;
	else
	{
		max1 = MaxBranch(b->lchild);
		max2 = MaxBranch(b->rchild);
		if (max1 > max2) max = max1 + 1;
		else max = max2 + 1;
		return max;
	}
}

运行结果

在这里插入图片描述

7. 将二叉树的顺序存储结构转换成二叉链存储结构

  • 算法思想:设二叉树的顺序存储结构为a,由f(a,i)返回创建的以a[i]为根结点的二叉链存储结构(初始调用为b=f(a,1))。
BiTNode* trans(SqTree t[], int i)
{
	BiTNode* b;
	if (i > MaxSize) return NULL;
	if (t[i].value == '#') return NULL;
	b = (BiTNode*)malloc(sizeof(BiTNode));
	b->data = t[i].value;
	b->lchild = trans(t, 2 * i);
	b->rchild = trans(t, 2 * i + 1);
	return b;
}
trans(t, 1);

运行结果

i123456789101112131415
valueDABEFCG####H##I

在这里插入图片描述
在这里插入图片描述

8. 将二叉树的二叉链存储结构转换成顺序存储结构

void trans(BiTNode* b, ElemType a, int i)
{
	if (b == NULL) a[i] = '#';
	else
	{
		a[i] = b->data;
		trans(b->lchild, a, 2 * i);
		trans(b->rchild, a, 2 * i + 1);
	}
}
tans(b,a,1);

9. 假设二叉树采用二叉链表存储结构,求二叉树的高度。

  • 方法一(非递归算法)

  • 算法思想:采用后序遍历的方法,当处理到最深层(h层)的叶子结点时,其h-1层的所有结点都在栈中,当前栈的深度即为二叉树的高度。
int PostOrder(BiTNode* b)
{
	BiTNode* p, * r;
	bool flag;
	int count = 0, max = 0;
	SqStack* st; //定义一个顺序栈指针st
	InitStack(st); //初始化栈st
	p = b;
	do {
		while (p != NULL) //扫描结点p的所有左下结点并进栈
		{
			Push(st, p); //结点p进栈
			count++; //入栈则高度+1
			if (count > max) max = count;
			p = p->lchild; //移到到左孩子
		}
		r = NULL; //r指向刚访问的结点,初始时为空
		flag = true; //flag为真表示正在处理栈顶结点
		while (!EmptyStack(st) && flag)
		{
			GetTop(st, p); //取出当前的栈顶结点p
			if (p->rchild == r) //若结点p的右孩子为空或者为刚刚访问过的结点
			{
				printf("%c ", p->data); //访问结点p
				Pop(st, p);
				count--; //出栈则高度-1
				r = p; //r指向刚访问过的结点
			}
			else
			{
				p = p->rchild; //转向处理其右子树
				flag = false; //表示当前不是处理栈顶结点
			}
		}
	} while (!EmptyStack(st)); //栈不空循环
	printf("\n");
	//DestroyStack(st); //销毁栈
	return max;
}

运行结果

在这里插入图片描述
在这里插入图片描述

  • 时间复杂度为O(n),空间为O(max)。其中,n为二叉树中的结点个数,max为栈的最大深度。
  • 方法二(非递归算法)

  • 算法思想:利用层序遍历思想,记录层数。rear表示每层中最后一个结点的序号,front表示当前访问结点的序号。
int LevelOrder(BiTNode* b)
{
	int front = 0, rear = 0, last = 1, level = 0; //last指向当前层的最右结点
	if (b == NULL) return 0; //空树返回0
	BiTNode* p;
	SqQueue* qu;
	InitQueue(qu);
	enQueue(qu, b); //根节点入队
	rear++;
	while (!EmptyQueue(qu)) //队不空,则循环
	{
		
		deQueue(qu, p); //队列元素出队,即正在访问的结点
		front++;
		printf("%c ", p->data);
		if (p->lchild != NULL) //左孩子入队
		{
			enQueue(qu, p->lchild);
			rear++;
		}
		if (p->rchild != NULL) //右孩子入队
		{
			enQueue(qu, p->rchild);
			rear++;
		}
		if (front == last) //处理该层的最右结点
		{
			level++; //层数+1
			last = rear; //last指向下层
		}
	}
	return level;
}

运行结果

在这里插入图片描述

  • 方法三(递归算法)

int Height(BiTNode* b)
{
	int left, right;
	if (b == NULL) return 0;
	left = Height(b->lchild);
	right = Height(b->rchild);
	return left > right ? left + 1 : right + 1;
}

运行结果

在这里插入图片描述

  • 求某层的结点个数、每层的结点个数、树的最大宽度等,都采用与此题类似的思想。

①某层的结点个数(非递归)

int LevelOrder(BiTNode* b,int i) //求二叉树的高度
{
	//front表示当前层第一个结点的序号,last表示当前层最后一个结点的序号
	//rear表示下一层的最后一个结点的序号,visit表示当前访问结点的序号
	int front = 1, rear = 0, last = 1, level = 0, visit = 0;
	if (b == NULL || i <= 0) return 0; //空树或参数不正确时返回0
	if (b != NULL && i == 1) return 1;
	BiTNode* p;
	SqQueue* qu;
	InitQueue(qu);
	enQueue(qu, b); //根结点入队
	rear++;
	while (!EmptyQueue(qu)) //队不空,则循环
	{
		deQueue(qu, p); //队列元素出队,即正在访问的结点
		visit++; //visit表示当前正在访问的结点的序号
		//printf("%c ", p->data);
		if (p->lchild != NULL)
		{
			enQueue(qu, p->lchild); //左孩子入队
			rear++;
		}
		if (p->rchild != NULL)
		{
			enQueue(qu, p->rchild); //右孩子入队
			rear++;
		}
		if (visit == last) //如果当前访问的结点是当前层中最右侧的结点
		{
			level++; //层数+1
			if (level == i)
			{
				printf("front=%d last=%d", front, last);
				return last - front + 1;
			}
			front = last + 1; //下一层的第一个结点序号为上一层最后一个结点序号+1
			last = rear; //修改下一层最后一个结点的序号
		}
	}
}

运行结果:在这里插入图片描述

①某层的结点个数(递归)

  • 设计算法为Lnodenum(BiTNode* b, int h, int k, int& n),h表示b所指的结点层次,n是引用型参数,用于求第k层的结点个数。在初始调用时,b为根结点指针,h为1,n赋值为0,即调用方式是n=0;Lnodenum(b,1,k,n)。
void Lnodenum(BiTNode* b, int h, int k, int& n)
{
	if (b == NULL) return; //空树直接返回
	else //处理非空树
	{
		if (h == k) n++; //当前访问的结点在第k层时n增1
		else if (h < k) //若当前结点层次小于k,递归处理左、右子树
		{
			Lnodenum(b->lchild, h + 1, k, n);
			Lnodenum(b->rchild, h + 1, k, n);
		}
	}
}

②每层的结点个数(非递归)

int LevelOrder(BiTNode* b) //求二叉树的高度
{
	//front表示当前层第一个结点的序号,last表示当前层最后一个结点的序号
	//rear表示下一层的最后一个结点的序号,visit表示当前访问结点的序号
	int front, rear = 0, last = 1, level = 0, visit = 0;
	if (b == NULL) return 0; //空树返回0
	BiTNode* p;
	SqQueue* qu;
	InitQueue(qu);
	enQueue(qu, b); //根结点入队
	rear++;
	while (!EmptyQueue(qu)) //队不空,则循环
	{
		deQueue(qu, p); //队列元素出队,即正在访问的结点
		visit++; //visit表示当前正在访问的结点的序号
		//printf("%c ", p->data);
		if (p->lchild != NULL)
		{
			enQueue(qu, p->lchild); //左孩子入队
			rear++;
		}
		if (p->rchild != NULL)
		{
			enQueue(qu, p->rchild); //右孩子入队
			rear++;
		}
		if (visit == last) //如果当前访问的结点是当前层中最右侧的结点
		{
			level++; //层数+1
			if(level == 1) front = last; //第一层只有根结点
			printf("front=%d last=%d", front, last);
			printf("-----第%d层有:%d个结点\n", level, last-front + 1);
			front = last + 1; //下一层的第一个结点序号为上一层最后一个结点序号+1
			last = rear; //修改下一层最后一个结点的序号
		}
	}
	return level;
}

运行结果:在这里插入图片描述

③树的最大宽度(非递归)

int LevelOrder(BiTNode* b) //求二叉树的高度
{
	//front表示当前层第一个结点的序号,last表示当前层最后一个结点的序号
	//rear表示下一层的最后一个结点的序号,visit表示当前访问结点的序号
	int front, rear = 0, last = 1, level = 0, visit = 0, maxwidth = 0;
	if (b == NULL) return 0; //空树返回0
	BiTNode* p;
	SqQueue* qu;
	InitQueue(qu);
	enQueue(qu, b); //根结点入队
	rear++;
	while (!EmptyQueue(qu)) //队不空,则循环
	{
		deQueue(qu, p); //队列元素出队,即正在访问的结点
		visit++; //visit表示当前正在访问的结点的序号
		//printf("%c ", p->data);
		if (p->lchild != NULL)
		{
			enQueue(qu, p->lchild); //左孩子入队
			rear++;
		}
		if (p->rchild != NULL)
		{
			enQueue(qu, p->rchild); //右孩子入队
			rear++;
		}
		if (visit == last) //如果当前访问的结点是当前层中最右侧的结点
		{
			level++; //层数+1
			if (level == 1) front = last; //第一层只有根结点
			printf("front=%d last=%d", front, last);
			printf("-----第%d层有:%d个结点\n", level, last - front + 1);
			if (last - front + 1 > maxwidth) maxwidth = last - front + 1; //比较每一层的结点个数
			front = last + 1; //下一层的第一个结点序号为上一层最后一个结点序号+1
			last = rear; //修改下一层最后一个结点的序号
		}
	}
	printf("\n二叉树的高度为:%d", level);
	return maxwidth;
}

运行结果:在这里插入图片描述

  • 6
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值