第五章学习小结
特殊的树
- 二叉排序树(二叉查找树/二叉搜索树):若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树
- 最小堆:是一种经过排序的完全二叉树,其中任一非终端节点的数据值均不大于其左子节点和右子节点的值
- 哈夫曼树(最优二叉树):给定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或者阅读其源码,虽然对