数据结构_二叉树(binary tree)基本定义、性质、理解总结+代码实现(C/C++)

二叉树(binary tree)基本定义、性质、理解、总结+代码实现

约定:层序编号:二叉树从根结点开始按照从上到下,从左到右的顺序从1开始给结点编号。

二叉树(binary tree)

定义:
一个有限的结点集合。这个集合或者为空,或者由一个根结点和两棵互不相交的称为左子树(subtree)和右子树的二叉树组成。

二叉树是一种特殊的树形数据结构,二叉树的定义也是递归的,这决定了对二叉树的算法设计往往设计递归思想

二叉树并不等于二次树,两者的区别:
二次树必须有一个度为2的结点,而二叉树无要求;
二叉树严格区分左右子树,而二次树无要求。


1. 二叉树的其中两种形态

(1)满(full)二叉树:
所有分支结点都有左右子树,并且叶子结点都集中在最高层。/ 高度为h且有2h-1个结点的二叉树(根据树的性质)。
特点:
所有叶子结点都在最高层;
只有度为0和2的结点。

(2)完全(complete)二叉树:
满足:
1) 最下面两层的结点的度小于等于2;
2) 最下面一层的叶子结点靠左排列。
的二叉树。
特点:
1)所有叶子结点只出现在最下面两层;
2)最下面一层的叶子结点依次排列在最左边;
3)如果有度为1的结点,只可能有一个,并且这个结点只有左孩子;
按层序编号时,一旦出现编号为i的结点是叶子结点或者是只有左孩子的结点,则编号大于i的结点都为叶子结点;
4)当结点总数是奇数时,n1=0,偶数时,n1=1。

两种形态的关系:满二叉树是完全二叉树的特例


2. 二叉树的性质

(1)二叉树的叶子结点数等于双分支结点数加1
证明:
由树的性质得:
n = n0 + n1 + n2(结点总数) = n1 + 2n2 + 1(所有结点度数之和加1 )
推出:n0 = n2 + 1

(2)同树的性质二(m次树第i层的最大结点数)

(3)同树的性质三(由树高h确定树的最大结点数)

(4)完全二叉树中层序编号为i的结点(1<= i <=n , n>=1,n为结点数)
有以下性质:
1) 若i <= (n/2)向下取整,则编号为i的结点为分支结点,否则为叶子结点;
性质1)等价于:完全二叉树中最后一个叶子结点的层序编号n除以2向下取整,将得到最后一个分支结点的层序编号;或者这样理解:某结点的双亲结点的层序编号可由其编号除以2向下取整得到。
2) 若n为奇数,则每个分支结点都有左右孩子,若为偶数,则最大编号分支结点只有左孩子;
奇数的情况:一个根结点 + 偶数个孩子结点(后继结点)
偶数的情况:一个根结点 + 偶数个右孩子结点 + 奇数个左孩子结点
3)编号为i的结点的左孩子结点编号为2i,右孩子编号为2i+1;
4)编号为i的结点的双亲结点编号为(i/2)向下取整
(5)具有n个(n>0)结点的完全二叉树的高度为(log2(n+1))向下取整,或(log2n)向上取整加1


3. 二叉树的存储结构

(1) 顺序存储结构

存储结构实现:一维数组,
结点类型声明实现代码:

#define MaxSize 50
typedef char ElemType;
typedef ElemType SqBTree[MaxSize];

定义并初始化一棵二叉树:

SqBTree btree = {'', 'A', 'B', 'C', 'D'};

为了对应二叉树的层序编号,在C/C++中,数组首元素位置不用,因此根结点在数组中的编号——下标1,对应逻辑结构中的层序编号——1。

适用情况:完全二叉树、满二叉树

不太适用:一般二叉树
因为既要存储结点值又要存储结点之间的关系,因此不能直接按顺序将结点存储到数组中,而要采用方法:先用空结点补全为一棵完全二叉树,然后进行层序编号,再将非空结点存储到对应编号的数组位置上。这会导致的问题是:有些二叉树的形态会浪费较大或极大的存储空间。如一棵树高为h的右单支树,实际结点有h个,但需要2h-1个结点空间存储。

优点:数组下标对应逻辑结构的层序编号,查找某类结点可直接计算得出,如编号为i的结点的孩子结点(2i和2i+1)及双亲结(i/2向下取整)、确定编号为i的结点的所在层数等等。

缺点:顺序存储结构的固有缺陷:不便于插入和删除操作。

总结:顺序存储结构比较适合存储完全二叉树、满二叉树。

(2) 二叉链(binary link list)
二叉链基本概念:用结构体作为二叉树的结点,其中包含data域和左、右孩子指针域,这样一种存储结构
二叉链结点类型声明:

typedef struct node
{
	ElemType data;
	struct node *lchild;
	struct node *rchild;
}BTNode;

结点默认按层序编号的排序,结点之间的逻辑关系用指针域实现。

适用情况:所有形态的二叉树

优点:相对于顺序存储结构,二叉链的空间随情况而分配,具有很好的灵活性及很好的存储空间利用率,因此适用于二叉树的所有形态。

不足(作为链式存储结构):不具备随机存取特性,因为存储单元不是连续分配的,但因为树这种数据结构是递归的,所以大多数情况下可以采用递归算法实现对数据结点的访问。


4. 二叉树ADT

{
数据对象:
D = { ai | 1<=i<=n,n>=0 }
数据关系:
R = { <ai , aj> | 1<= i,j <=n }
基本运算:
CreateBTree(&b);
DestroyBTree(&b);
DispBTree(b);
BTreeHeight(b, n);
LChild(t,p);
RChild(t,p);
Parent(t,p);
……
}


5. 基本运算实现代码

(1)创建二叉树:

//利用栈创建二叉树
void CreateBTree(BTNode * &btree_p, char *str)
{
	BTNode *node_stack[50], *node;
	int top = -1, index = 0, flag;
	btree_p = NULL;  //尽量在定义指针时进行指针的初始化
	char ch = str[index];
	while(ch != '\0')
	{
		switch(ch)
		{
			case '(':
				top++;
				node_stack[top] = node;
				flag = 1;
				break;
			case ')':
				top--;
				break;
			case ',':
				flag = 2;
				break;
			default:
				node = (BTNode *)malloc(sizeof(BTNode));
				node->data = ch;
				node->lchild = node->rchild = NULL;  //一定要记得初始化指针域
				if(!btree_p)
					btree_p = node;
				else
				{
					switch(flag)
					{
						case 1:
							node_stack[top]->lchild = node;
							break;
						case 2:
							node_stack[top]->rchild = node;
							break;
					}
				}
				break;
		}
		index++;
		ch = str[index];
	}
}

(2)销毁:

//递归销毁二叉树
void DestroyBTree(BTNode * &btree_p)
{
	if(btree_p != NULL)
	{
		DestroyBTree(btree_p->lchild);
		DestroyBTree(btree_p->rchild);
		free(btree_p);
	}
}

(3)输出二叉树(括号表示法形式):

void DispBTree(BTNode *btree_p)
{
	if(btree_p != NULL)
	{
		printf("%c",btree_p->data);
		if(btree_p->lchild != NULL
		|| btree_p->rchild != NULL)
		{
			printf("(");
			DispBTree(btree_p->lchild);
			if(btree_p->rchild)
				printf(",");
			DispBTree(btree_p->rchild);
			printf(")");
		}
	}
}

(4)前序遍历输出二叉树(中、后序遍历同理):

void PreTravel(BTNode *btree_p)
{
	if(btree_p != NULL)
	{
		printf("%c",btree_p->data);
		PreTravel(btree_p->lchild);
		PreTravel(btree_p->rchild);
	}
#if 0  
此乃递归出口,因为函数返回值是void类型,因此可缺省,由编译器默认执行
	else
		return ;
#endif
}

这里只列出最基本的运算,其他运算的实现就不再一一列出,有需要的朋友可以去我的GitHub上查阅。

最后附上:GitHub源码地址

有问题的朋友欢迎私信交流

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值