No39. 二叉树总结

4 篇文章 0 订阅
3 篇文章 0 订阅

1.定义

1.二叉树:一颗二叉树由结点的有限集合组成,这个集合或者为空,或者由一个根结点及两颗不相交的二叉树组成。
注意:左右子树不相交,说明它们没有公共的节点,公共的节点意思是数值相同,指针域也相同。

2.满二叉树:满二叉树的每一个结点或者是一个分支结点(非叶结点),并恰好有两个非空子结点。或者是叶结点。
1

3.完全二叉树:从根结点起每一层从左至右填充,一颗高度为d的完全二叉树除了底层(最后一层,即d-1层)以外,每一层都是满的。底层的叶子结点集中在左边的若干位置上。

2

2.名词区分

高度,深度,层数
结点M的深度(depth)就是从根结点到M的路径长度。数的高度(height)等于最深结点的深度加1,任何深度为d的结点的层数也是d,根结点的层数和深度都是0

3.一些定理和结论

  • 一棵有 n n n个结点的二叉树的形态总共有 C 2 n n n + 1 种 {{C^n_{2n}}\over {n+1}}种 n+1C2nn
    4
    (详细介绍:卡特兰数及其应用

  • 满二叉树定理:非空满二叉树的叶结点数等于其分支结点数加1
    (简单理解:假设一棵有n-1个分支结点的二叉树满足上面的结论,当某棵二叉树有n个分支结点时,选中一个分支结点,这个分支结点的左右子结点都是叶子结点,然后把这2个叶子结点都删掉(后果是删了一个分支结点,删了一个叶子结点),形成的新的二叉树满足之前的归纳假设,所以原二叉树也满足结论。)

    推论:一颗非空二叉树空子树的数目等于 其结点数目加1

4.遍历

先根遍历

2
(图片来源:https://images.app.goo.gl/fdMEerRMHvwLd46Z7)

后根遍历和中根遍历也是顾"名"思"义"的。

(1)由中根遍历和先根遍历确定树的唯一形态

简要思路(证明)

设 中 根 遍 历 的 结 果 是 : A 1 , A 2 , A 3 , . . . , A k 设 先 根 遍 历 的 结 果 是 : B 1 , B 2 , B 3 , . . . , B k 归 纳 假 设 1 , 2 , 3 , . . . , k − 1 个 节 点 可 以 的 先 根 和 中 根 可 以 构 成 一 棵 唯 一 树 ( k ≥ 2 ) 则 B 1 必 然 的 根 结 点 , 设 B 1 节 点 对 应 于 中 根 遍 历 的 A m ( 1 ≤ m ≤ k ) 进 一 步 得 : A 1 , A 2 , . . . , A m − 1 为 根 结 点 的 左 子 树 A m + 1 , A m + 2 , . . . , A k 为 根 结 点 的 右 子 树 B 2 , B 3 , . . . , B m 为 根 结 点 的 左 子 树 B m + 1 , B m + 2 , . . . , B k 为 根 结 点 的 右 子 树 根 据 归 纳 假 设 , 上 面 的 左 子 树 和 右 子 树 都 可 以 唯 一 确 定 , 因 此 整 棵 树 ( 有 k 个 结 点 ) 可 以 唯 一 确 定 \begin{aligned} &设中根遍历的结果是:A_1,A_2,A_3,...,A_k\\ &设先根遍历的结果是:B_1,B_2,B_3,...,B_k\\ &归纳假设1,2,3,...,k-1个节点可以的先根和中根可以构成一棵唯一树(k\geq2)\\ &则B_1必然的根结点,设B_1节点对应于中根遍历的A_m(1\leq m\leq k)\\ &进一步得:\\ &A_1,A_2,...,A_{m-1}为根结点的左子树\\ &A_{m+1},A_{m+2},...,A_k为根结点的右子树\\ &B_2,B_3,...,B_m为根结点的左子树\\ &B_{m+1},B_{m+2},...,B_k为根结点的右子树\\ &根据归纳假设,上面的左子树和右子树都可以唯一确定,\\ &因此整棵树(有k个结点)可以唯一确定\\ \end{aligned} A1,A2,A3,...,AkB1,B2,B3,...,Bk1,2,3,...,k1(k2)B1B1Am(1mk):A1,A2,...,Am1Am+1,Am+2,...,AkB2,B3,...,BmBm+1,Bm+2,...,Bk(k)

代码:

下面代码中用哈希表查找上面证明中m的值,查找的时间复杂度是O(1),否则顺序查找会要O(n)的时间,n为结点个数。
算法(构造唯一树)的整体时间复杂度是O(n)

#include <stdio.h>
#include <stdlib.h>
#define LEN sizeof(Tree)
#define NULLKEY -32768 
typedef struct Tree {
	char element;
	Tree* left;
	Tree* right;
};
void preOrder(Tree* root) {
	if (root == NULL)return;

	printf("%c ", root->element);
	preOrder(root->left);
	preOrder(root->right);
}
void inOrder(Tree* root) {
	if (root == NULL)return;

	inOrder(root->left);
	printf("%c ", root->element);
	inOrder(root->right);
}
void postOrder(Tree* root) {
	if (root == NULL)return;

	postOrder(root->left);
	postOrder(root->right);
	printf("%c ", root->element);
}
void freeAll(Tree* root) {
	if (root == NULL)return;

	freeAll(root->left);
	freeAll(root->right);
	free(root);
}
typedef struct
{
	int* elem; 						//基址
	int count; 						//当前数据元素个数 
}HashTable;

int Search(char* Array, HashTable* hashTable, int data);

//核心函数
Tree* strucrTree(char* pre, char* in, int start,int end,HashTable& ht) {
	//index记录先根遍历的父节点位置,最开始是根结点
	static int index = 0;
	if (start > end)
		return NULL;
	Tree* root = (Tree*)malloc(sizeof(Tree));
	root->element = pre[index++];
	root->left = NULL;
	root->right = NULL;
	if (start == end)
		return root;
	//in_index记录index对应节点在中根遍历中的位置
	int in_index = Search(in,&ht, pre[index]);
	root->left = strucrTree(pre, in, start, in_index - 1, ht);
	root->right = strucrTree(pre, in, in_index+1, end , ht);
	return root;
}

int m = 0; // 哈希表长度

/*初始化*/
int Init(HashTable* hashTable,int size)
{
	int i;
	m = 2*size;
	hashTable->elem = (int*)malloc(m * sizeof(int)); //申请内存
	hashTable->count = m;
	for (i = 0; i < m; i++)
	{
		hashTable->elem[i] = NULLKEY;
	}
	return 1;
}

/*哈希函数*/
int Hash(int data)
{
	return data % m;
}

/*插入*/
void Insert(HashTable* hashTable, int key,int value)
{
	int hashAddress = Hash(key); //求哈希地址

	//发生冲突
	while (hashTable->elem[hashAddress] != NULLKEY)
	{
		//利用开放定址的线性探测法解决冲突
		hashAddress = (++hashAddress) % m;
	}

	//插入值
	hashTable->elem[hashAddress] = value;
}

/*查找*/
int Search(char* Array,HashTable* hashTable, int data)
{
	int hashAddress = Hash(data); //求哈希地址

	//发生冲突
	while (Array[hashTable->elem[hashAddress]] != data)
	{
		//利用开放定址的线性探测法解决冲突
		hashAddress = (++hashAddress) % m;

		if (hashTable->elem[hashAddress] == NULLKEY || hashAddress == Hash(data)) return -1;
	}

	//查找成功
	return hashTable->elem[hashAddress];
}
int main() {
	char preorder[8] = { '1','2','4','6','5','7','3','8' };
	char inorder[8] = { '6','4','2','5','7','1','3','8' };
	int length = sizeof(inorder);
	Tree* root = (Tree*)malloc(LEN);
	HashTable hashtable;
	Init(&hashtable, length);
	for (int i = 0; i < length; ++i) {
		Insert(&hashtable, inorder[i],i);
	}
	root = strucrTree(preorder, inorder, 0,length-1, hashtable);
	preOrder(root);
	freeAll(root);
}

5.二叉树的应用

1.二叉检索树
2.表达式树
3.利用最大堆实现优先队列
4.最小堆和哈夫曼编码

6.Reference

1.《数据结构与算法分析(C++版)(第三版)》第5章 二叉树
2.https://images.app.goo.gl/fdMEerRMHvwLd46Z7

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值