【SUFE】数据结构作业——树

这是上海财经大学数据结构课程关于树的作业,涉及树的各种操作,如结点交换、后序遍历、有序树的转换、完全二叉树判断等。通过C++实现,包括前序、中序、后序遍历,以及二叉树的相似性检查等算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作业04 树

第一题

题目

假设二叉树T是按标准形式存贮的,试编写一个把该树中每个结点的左右子结点进行对换的C++函数。

思路

  1. 此处的“标准形式”为数据+指向子结点的指针

  2. 利用递归,对根结点下面的两个子结点进行交换。返回条件是根结点为空。

源程序清单

//node的结构
struct node {
	int data;
	node* leftChild;	//左子结点
	node* rightChild;	//右子结点
};


//交换结点
//从上到下的顺序换
void swapBinaryTree(node* &head) {
	if (head == NULL) return;   //basecase
	swap(head->leftChild, head->rightChild);	//先换上面的,把它移到最下面也可以
	swapBinaryTree(head->leftChild);
	swapBinaryTree(head->rightChild);
}

第二题

题目

编写一个利用来实现后序遍历一棵给定的二叉树的c++函数

思路

  1. 题目说利用栈,也就是说可以利用递归
  2. 后序遍历的顺序:左右根
  3. 遍历整棵树等于遍历根结点的左、右子树,就这样一直分到最小

源程序清单

//node的结构
struct node {
	int data;
	node* leftChild;	//左子结点
	node* rightChild;	//右子结点
};

//利用栈(即递归)实现后序遍历
void printBinaryTree(node* head) {
	if (head == NULL) {	//某结点为空,返回
		return;
	}

	//左后中的顺序输出
	printBinaryTree(head->leftChild);	
	printBinaryTree(head->rightChild);
	cout << head->data << " ";
}

第三题

题目

如果T是一棵有序树,T’是与T相对应的二叉树。假定T’已按标准形式被存贮,试为下面各小题分别编写一个C++函数:
(1)按前序输出树T的结点值。
(2)按后序输出树T的结点值。
(3)输出树T的叶子结点值。
(4)求出树T的次数。

思路

  1. 前面两问就是前、后序遍历并输出,与第二题思路基本一致。
  2. 第三问涉及到树转化为二叉树后,叶子结点的变化。下面是一颗树及其对应的二叉树:
    treeToB
    因为在转化过程中,
  • 树的非左子结点变成了它左子结点及其右子结点(h从e的右子结点变成了e的左子结点)
  • 兄弟结点转化为了它的右子结点(c、d从a的右子结点变成了b的右子结点)。
  • 左子结点照旧
  • 总之,只要它有子结点,一定是先变成它的左子结点,再变成其他的。
    所以,如果二叉树的一个结点没有左子结点,说明它既没有右子结点,又没有左子结点,即它是叶子结点。
    于是,我们只需要看二叉树的一个结点是不是没有左子结点,没有就说明它是树的叶子结点。
  1. 第四问求次数。已知一个树的次数等于它最多的某个结点的子结点数,由于树变成二叉树时,兄弟结点变成了它的连续右子结点。所以我们看二叉树的最大的连续右子结点的数量就可以求出次数了。

源程序清单


//计算当前结点的最大右连续子结点
// 从自己开始算起,所以head也算数,所以sum初始值为1
int countConRight(node* head) {
	int sum = 1;
	if (head->rightChild != NULL)	// 有右子结点就计数并往右走
		sum += countConRight(head->rightChild);
	else	// 没有右子结点了,作为最大值返回
		return sum;

}

//计算树的次数
//树的次数就是二叉树的左孩子的连续右孩子(包括自己)的最大值
int countTimes(node* head) {
	if (head == NULL) return 0;
	
	int num = countConRight(head);

	//应该还有更优的方法
	//取当前结点的最大右连续数和左右子树的进行比较,取最大值
	//由于根结点不可能有右子结点,所以不用对特殊情况进行处理。
	num = max(max(countTimes(head->rightChild),countTimes(head->leftChild)),num);
	return num;
}

第四题

题目

假设二叉树T的结点值是字符,已知树T中结点的前序和中序,试编写一个把树T按标准形式进行存贮的C++函数。

思路

  1. 首先,如果结点的值重复,会有多种情况,所以我们这里假设值不重复
  2. 前序的特点是:从左到右都是根结点;
    中序的特点是:某个结点的左右结点一定都在它的两边。
  3. 于是我们的思路就出来了:遍历前序,找到它在中序里面的位置,它左边就是左子树,右边就是右子树,这样就可以把中序不断分层成子树。
    比如,前序的第一个值为根结点,去中序中找到这个结点,它的左边就是根结点的左子树,右边就是根结点的右子树。接着在它的左边找到前序的第二个值的位置,又把它分成左右子树…(以下给出部分过程)
    Partitions
  4. 我们需要一个指针指向前序,在每次递归中都向后移一步;然后每次递归都把对应的左右子树当作“整棵树”传下去。

源程序清单

本程序参考了这篇博客

//t7,利用二叉树的前序和中序建立树
//由于如果有重复数值可能会有多种情况,所以我们这里假设没有重复数值
//前序的第一个为根节点,中序的根节点左右方分别是左右子树,所以可以利用这一点递归
node* makeTreeByPreIno(char pre[], char ino[], int n) {	//传入的两个参数分别为:前序、中序、数组长度
	if (n == 0) {	//当数组长度为0时,返回NULL
		return NULL;
	}

	// 1.首先取前序的首个作为根节点的值
	node* head = new node;
	head->data = pre[0];

	// 2.接着在中序里面找到这个值
	char* p = ino;	//作为寻找该值的指针
	for (; p < ino + n; p++) {
		if (*p == pre[0])	//找到根节点的位置(值不重复)
			break;
	}

	int leftNum = p - ino;	//左子树的大小,等于中序里面找到的根节点的位置减去开头的位置

	// 3.找到后,它(中序)的左边为左子树,右边为右子树,开始递归

	// 首先是左子树,pre[0]已经是根节点了,所以需要“后移”一位
	// 中序的“子树范围”受leftNum(左子树大小)限制,所以相当于ino和leftNum一起“截取”了该子树
	head->leftChild = makeTreeByPreIno(pre + 1, ino, leftNum);	

	// 接着是右子树,它的起始位置等于起点加左子树的大小。
	// 右子树的起点则为p+1,长度为总长减去左子树长(找到的根节点后面的部分)
	// 而长度则为剩下一部分的长度(传入的长度减一,因为前序的第一个已经是根节点了,所以总长度减一)减去左子树长度
	head->rightChild = makeTreeByPreIno(pre + 1 + leftNum, p + 1, n - leftNum - 1);	

	return head;
}

第五题

题目

在二叉树T中,如果非叶子结点都有两棵非空子树,那么称二叉树T是一棵完全二叉树。试编写一个判断给定的二叉树是否为完全二叉树的C++函数。

思路

  1. 题目说了,非叶子结点都有两棵非空子树,这就是个非常好的递归basecase。
  2. 只有左右子树均为完全二叉树才算是完全二叉树。而当某个结点左右子结点都为空时,我们就认为它是完全二叉树(只有一个结点的完全二叉树)。

源程序清单

//判断是不是完全二叉树
bool isCompleteBT(node* head) {
	if (head->leftChild == NULL && 
		head->rightChild == NULL) {	//左右子树均为空,说明是叶子结点,返回真
		return true;
	}
	else if (head->leftChild != NULL && head->rightChild != NULL) {	//如果左右子树均不为空,递归下去
		return isCompleteBT(head->leftChild) && isCompleteBT(head->rightChild);	
	}
	else
		return false;	//一个为空,一个不为空,不是完全二叉树
}

第六题

题目

现给出两棵二叉树相似的定义如下:如果这两棵二叉树皆为空;或者这两棵二叉树的根结点的左子树和右子树是分别相似的,那么我们称这两棵二叉树是相似的。试编写一个判断两棵给定的二叉树是否相似的C++函数。

白话:判断两棵二叉树是不是结构一样。

思路

  1. 根据题目,很明显可以使用递归。两树均为空、左右子树相似为相似,其他不相似。
  2. 本题和上一题基本思路一致,区别只在于判断条件的更改。

源程序清单

//判断两树是否相似
bool isSimilarTree(node* tree1, node* tree2) {
	if (tree1 == NULL && tree2 == NULL)	//两棵子树为空,说明相似
		return true;
	else if (tree1 != NULL && tree2 != NULL)	//没有为空的子树就接着判断子树的相似;
		return isSimilarTree(tree1->leftChild, tree2->leftChild)
		&& isSimilarTree(tree1->rightChild, tree2->rightChild);	//左右子树是否分别相似
	else	//如果一个为空一个不为空,就说明不相似
		return false;
}

  
  
  
  
想看以下题目解析请点我

  
  
  

第七题

题目

设二叉树T已按前序附带一个标志位和一个右指针的顺序存贮方法存放,试编写一个把树T转换成由标准形式进行存贮的树T’的C++函数。

第八题

题目

假设二叉树T已按前序附加两个标志位的顺序存贮方法存放,且a是T中一个结点的值,试编写一个寻找结点a的父结点的C++函数。

第九题

题目

假设二叉树T是一棵穿线树,试编写一个按前序遍历树T的C++函数。(不允许用递归程序,也不使用数组,只允许增加个别变量)

ps:此处说的穿线树是指线索二叉树,在本题中指按中序顺序的线索二叉树。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值