数据结构与算法知识整理(三)--- 树与二叉树

本篇内容以知识整理为主,会结合萨特吉-萨尼的数据结构书籍和网络上的一些知识整理做一下总结,语言使用c++,有问题请及时指正,欢迎交流。

1、关于树的一些定义

1、树t是一个非空的有限元素的集合,其中一个元素为(root),余下的元素组成t的子树(subtree)。
2、层次关系:层次中最高层的元素为根。其下一级的元素是余下元素所构成的子树的根。
3、树的表示:在画一棵树时,每个元素都代表一个节点。树根在上面,其子树画在下面。在树根与其子树的根(如果有子树)之间有一条边。同样的,每一棵子树也是根在上,子树在下。在一棵树中,边连结一个元素及其子节点。
在这里插入图片描述
4、相关概念:画一棵树时,每个元素都代表一个节点。在树中没有孩子的元素称为叶子。指定树根的为1,其孩子(如果有)的级为2,孩子的孩子为3等等。元素的度是指其孩子的个数,叶节点的度为0。树的度是指其元素度的最大值。树的深度是节点的最大级。

2、二叉树的相关概念

1、二叉树的定义

二叉树(binary tree)t是有限个元素的集合(可以为空)。当二叉树非空时,其中又一个称为根的元素,余下的元素(如果有的话)被组成两个二叉树,分别称为t的左子树右子树

2、二叉树与树的区别

1、二叉树可以为,但树不能为空。
2、二叉树中每个元素都恰好有两棵子树(其中一个或两个可能为空),而树中的每个元素可有若干个子树。
3、在二叉树中每个元素的子树都是有序的,也就是说,可以用左、右子树来区别,而输的子树间是无序的。

3、二叉树的基本形态

a)空 ; b)仅有根节点;c)仅有左子树;d)仅有右子树;e)左右子树都有。
在这里插入图片描述在这里插入图片描述在这里插入图片描述

4、二叉树的特性

1、一棵二叉树有n个元素,n>0,它有n-1条边。
2、一棵二叉树的高度为h,h>=0,它最少有h个元素,最多有pow(2,h)-1个元素。//2的h次方减1个元素,不会打。
3、一棵二叉树有n个元素,n>0,它的最大高度为n,最小高度为log2[(n+1)]。

5、满二叉树与完全二叉树

1、当高度为h的二叉树恰好有pow(2,h)-1个元素时,称其为满二叉树
在这里插入图片描述
2、一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树
在这里插入图片描述
3、k层完全二叉树:前k-1层为满二叉树;第k层上的节点都连续排列于第k层的左端。
4、设完全二叉树中一元素的序号为i, 1≤i≤n。则有以下关系成立:
1) 当i = 1时,该元素为二叉树的根。若i > 1,则该元素父节点的编号为「i / 2 」。
2) 当2i >n时,该元素无左孩子。否则,其左孩子的编号为2i。
3) 若2i + 1 >n,该元素无右孩子。否则,其右孩子编号为2i + 1。

3、二叉树数组描述

二叉树可以作为缺少了部分元素的完全二叉树,一棵有n个元素的二叉树可能最多需要pow(2,n)-1个空间来存储,当每个节点都是其他节点的右孩子时,存储空间达到最大。
在这里插入图片描述

4、二叉树的链表描述

二叉树最常用的描述方法是用链表或指针。
每个元素都用一个有两个指针域的节点表示,这两个域为LeftChild和RightChid。除此两个指针域外,每个节点还有一个data域
二叉树的边可用一个从父节点到子节点的指针来描述。指针放在父节点的指针域中。
在这里插入图片描述

二叉树的构造及初始化

struct TreeNode {
	int data;//假设数据为int型
	TreeNode *leftchild;
	TreeNode *rightchild;
};

void CreateBiTree(TreeNode *T)
{
	int ch;
	cin >> ch;
	if (ch == '#')
		T = NULL;  //保证是叶结点
	else
	{
		T = new(TreeNode);
		//if (!*T)
			//exit(OVERFLOW); //内存分配失败则退出。
		T->data = ch;//生成结点
		CreateBiTree(T->leftchild);//构造左子树
		CreateBiTree(T->rightchild);//构造右子树    
	}

5、二叉树的遍历

1、前序遍历

// 前序遍历:先访问根节点,再访问左子树,最后访问右子树
// 递归方式
void preTraversal(TreeNode* root) {
	if (root != NULL) {
		cout << root->data;
		preTraversal(root->leftchild);
		preTraversal(root->rightchild);
	}
}
/* 非递归:用栈来实现,首先访问根节点,然后将根节点入栈
接着访问当前节点的左节点,然后左节点入栈,左节点访问完之后出栈
再依次访问右节点 */
void un_preTraversal(TreeNode* root) {
	stack<TreeNode*> stack;
	TreeNode* p = root;
	while (p != NULL || stack.size() != 0) {
		if (p != NULL) {
			cout << p->data;
			stack.push(p);
			p = p->leftchild;
		}
		else {
			p = stack.top();
			stack.pop();
			p = p->rightchild;
		}
	}
}

2、中序遍历

//中序遍历:先访问左子树,再访问根节点,最后访问右子树
//递归
void midTraversal(TreeNode* root) {
	if (root != NULL) {
		midTraversal(root->leftchild);
		cout << root->data;
		midTraversal(root->rightchild);
	}
}
//非递归-中序遍历
/*
思想:用栈来实现。从根节点开始依次遍历当前节点的左节点,并依次入栈,当左节点遍历完成后,获取
栈顶元素并出栈,然后访问该节点,并依次遍历其右节点
*/
void un_midTraversal(TreeNode* root) {
	TreeNode* p = root;
	stack<TreeNode*> stack;
	while (p != NULL || stack.size() != 0) {
		if (p != NULL) {
			stack.push(p);
			p = p->leftchild;
		}
		else {
			p = stack.top();
			cout << p->data;
			stack.pop();
			p = p->rightchild;
		}
	}
}

3、后序遍历

//后序遍历:先遍历左子树,再遍历右子树,最后遍历根节点
//递归
void postTraversal(TreeNode* root) {
	if (root != NULL) {
		postTraversal(root->leftchild);
		postTraversal(root->rightchild);
		cout << root->data;
	}
}
//非递归 后序遍历
/*
思想:用栈来实现。先根节点开始依次遍历左节点,已经遍历过了的标记为'l',
然后依次遍历右节点,遍历过的标记为'r',
只有当标记为'r'时才能访问该节点。
*/

//定义一个有标记的结构体
typedef struct MarkNode {
	TreeNode* node;//树的节点的指针
	char tag;
}MarkNode;

void un_postTraversal(TreeNode* root) {
	TreeNode* p = root;
	MarkNode *n;
	stack<MarkNode*> stack;
	while (p != NULL || stack.empty() == false)
	{
		//遍历左节点并标记
		while (p != NULL)
		{
			n = new MarkNode;
			n->node = p;
			n->tag = 'l';
			stack.push(n);
			p = p->leftchild;
		}

		//出栈
		n = stack.top();
		stack.pop();

		//遍历当前节点的右子树
		if (n->tag == 'l')
		{
			n->tag = 'r';
			//再次入栈
			stack.push(n);
			//此时p==NULL,一定要给p当前的节点
			p = n->node;
			p = p->rightchild;
		}
		//左右子树遍历完成后访问该节点
		else
		{
			cout<<n->node;
			//并把p置空
			p = NULL;
		}
	}
}

4、层次遍历

//层次遍历:使用队列queue,先将根节点入队,循环判断当前队列不为空时
//将头元素出队并访问头元素,然后将它的左节点和右节点入队列
void levelTraversal(TreeNode* root) {
	queue<TreeNode*> q;
	TreeNode *p = root;
	q.push(p);
	while (q.empty()==false)
	{
		p = q.front();
		q.pop();
		cout << p->data;
		if (p->leftchild != NULL) {
			q.push(p->leftchild);
		}
		if (p->rightchild != NULL) {
			q.push(p->rightchild);
		}
	}
}

6、二叉树的常用操作

1、确定其高度。
2、确定其元素数目。
3、复制。
4、显示二叉树。
5、确定两棵二叉树是否一样。
6、删除整棵树。
7、若为数学表达式树,计算该数学表达式。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值