第五章学习小结

特殊的树

  1. 二叉排序树(二叉查找树/二叉搜索树):若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树
  2. 最小堆:是一种经过排序的完全二叉树,其中任一非终端节点的数据值均不大于其左子节点和右子节点的值
  3. 哈夫曼树(最优二叉树):给定n个权值作为n个叶子结点,构造一棵二叉树,该树的带权路径长度达到最小。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

深入虎穴

实践课上进行了一道难题的讲解,心得如下
点击跳转:深深深深深深入虎穴

树的同构

因为偷懒就把思路写在注释里了,所以就直接贴上来

/*
主要思路是利用递归对两个树进行判断
对于这两棵树,有两个对应子树是同构的,两棵树不一定是同构的
但有两个对应子树不是同构的,两棵树就一定不是同构的
所以在递归过程中,只要出现不同构,就可以退出,判断不同构的
如果是同构,则继续递归进行比较
需要对以下情况进行判断:
1. 两棵树为空:同构
2. 空树和非空树:不同构
3. 两棵树根不同:不同构
由于我们先从左子树判断,所以先对左结点分析
4. 左结点为空:比较右结点
5. 左结点不为空:当两棵树左结点相同时,分别比较两棵树的左子树和右子树是否相同
以上都是对两棵树的结点是否相同进行的判断,也就判断两棵树各个结点是否相等
还没有进行是否同构的判断。要怎么判断是否同构呢?
同构是左右结点互换形成的,本质上是判断一棵树的左结点是否和另一颗树的右结点是否相等
因此当树不相同时(不是4、5),要对左右结点进行判断
*/

#include <iostream>

using namespace std;

//结点定义
typedef struct node {
	char name;
	char lchild;
	char rchild;
}Tree;

int create(Tree tree[], int n);
bool isSame(Tree tree1[], Tree tree2[], int root1, int root2);

int main() {
	int m, n, root1, root2;
	bool result = false;
	Tree tree1[10], tree2[10];
	cin >> m;
	root1 = create(tree1, m);
	cin >> n;
	root2 = create(tree2, n);
	if (m != n) {	//结点数目不相等则不同构
		result = false;
	}
	else if (m == 0 && n == 0) {	//两颗空树为同构
		result = true;
	}
	else if (isSame(tree1, tree2, root1, root2)) {
		result = true;
	}

	if (result) {
		cout << "Yes";
	}
	else {
		cout << "No";
	}
	return 0;
}

//输入,同时得到根结点下标
int create(Tree tree[], int n) {
	int a[10] = { 0 };	//用来记录作为输入中作为叶子结点出现的结点从而得到根结点
	int result = 0;
	if (n == 0) {	//空表,避免野指针
		tree = NULL;
	}
	else {		//输入结点构建树
		for (int i = 0; i < n; i++) {
			cin >> tree[i].name >> tree[i].lchild >> tree[i].rchild;
			if (tree[i].lchild != '-') {
				a[tree[i].lchild - '0'] = 1;
			}
			if (tree[i].rchild != '-') {
				a[tree[i].rchild - '0'] = 1;
			}
		}
		//循环得出根结点
		for (int i = 0; i < n; i++) {
			if (a[i] == 0) {
				result = i;
				break;
			}
		}
	}
	return result;
}

bool isSame(Tree tree1[], Tree tree2[], int root1, int root2) {
	//子树为空
	if ( root1 == '-' - '0' && root2 == '-' - '0') {	
		return true;
	}
	//空树和非空树
	if ((root1 == '-' - '0' && root2 != '-' - '0') && (root1 != '-' - '0' && root2 == '-' - '0')) {
		return false;
	}
	//根结点不同
	if (tree1[root1].name != tree2[root2].name) {
		return false;
	}
	//左子树为空,比较右子树
	if (tree1[root1].lchild == '-'&&tree2[root2].lchild == '-') {
		return isSame(tree1, tree2, tree1[root1].rchild - '0', tree2[root2].rchild - '0');
	}
	//左子树相同
	if ((tree1[root1].lchild != '-'&&tree2[root2].lchild != '-') && (tree1[tree1[root1].lchild - '0'].name == tree2[tree2[root2].lchild - '0'].name)) {
		return isSame(tree1, tree2, tree1[root1].lchild - '0', tree2[root2].lchild - '0') && isSame(tree1, tree2, tree1[root1].rchild - '0', tree2[root2].rchild - '0');
	}
	else {		//比较左右、右左子树
		return isSame(tree1, tree2, tree1[root1].lchild - '0', tree2[root2].rchild - '0') && isSame(tree1, tree2, tree1[root1].rchild - '0', tree2[root2].lchild - '0');
	}
}

心得与目标

让我印象最深的还是树的逻辑结构与存储结构不必一致。很多情况下可以用数组而非二叉链表,会让程序的实现方便很多。
比较遗憾的是之前的目标没有很好地达成,没有很好地学习stl或者阅读其源码,虽然对基本的操作都有涉及,但不应该仅仅停留于“我会用”的境地,而应该要知道“为什么这么用”。因此将其拟定为这次目标。相对的,我阅读了《高质量C++编程指南》,受到了比较大的触动(前言比正文好看)。我自认为我的编程习惯很好,却还是有很多不足的地方,令人惭愧。因此对这本书还打算有进一步的阅读。

原文地址:https://www.cnblogs.com/luoyang0515/p/10808940.html