树与二叉树

树与二叉树

  • 一、树
    • 1、树的概念
    • 2、关于树的注意点
    • 3、树的结构图
    • 4、树的相关概念
  • 二、二叉树
    • 1、二叉树的概念
    • 2、二叉树的特点
    • 3、二叉树的结构图
    • 4、特殊的二叉树
    • 5、二叉树的性质
    • 6、二叉树的存储结构
      • (1)顺序存储
      • (2)顺序存储结构图
      • (3)链式存储
      • (4)链式存储结构图(二叉链)
  • 三、二叉树的遍历
    • 1、概念与三种遍历方式
    • 2、前序遍历的代码
  • 四、二叉树创建函数的实现
    • 1、二叉树的链式存储结构体
    • 2、二叉树创建前的赋值
    • 3、代码
    • 4、实现原理
  • 五、二叉树销毁函数的实现
    • 1、代码
    • 2、实现原理
    • 3、注意点
  • 六、二叉树的层序遍历
    • 1、概念
    • 2、结构图
    • 3、代码
    • 4、实现原理
    • 5、实现原理图
    • 6、注意点

一、树

1、树的概念

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树的原因是它看起来像一棵倒挂着的树,也就是说它是根朝上,而叶朝下的。

2、关于树的注意点

  • 有一个特殊的结点,称为根结点,根节点没有前驱结点。
  • 除根节点外,其余结点被分成M (M>0) 个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。
  • 每棵子树的根结点有且只有一个前驱,可以有0个或多个后驱,因此,树是递归定义的。
  • 树形结构中,子树之间不能有交集,否则就不是树形结构
  • 除了根结点外,每个结点有且仅有一个父结点。
  • 一棵N个结点的树有N-1条边。

3、树的结构图

树与二叉树_第1张图片

4、树的相关概念

  • 节点的度:一个节点含有的子树个数称为该节点的度; 如上图:A的为6
  • 叶节点或终端节点:度为0的节点称为叶节点; 如上图:B、C、H、I…等节点为叶节点
  • 非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G…等节点为分支节点
  • 双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
  • 孩子节点或子节点:一个节点含有子树的根节点,则该子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点
  • 兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点
    树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6
  • 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
  • 树的高度或深度:树中节点的最大层次; 如上图:树的高度为4
  • 节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
  • 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
  • 森林:由m(m>0)棵互不相交的树组成的集合称为森林

二、二叉树

1、二叉树的概念

一棵二叉树是结点的一个有限集合,该集合为空或者由一个根节点加上两棵别称为左子树和右子树的二叉树组成。

2、二叉树的特点

  • 二叉树不存在度大于2的结点。
  • 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树。

3、二叉树的结构图

树与二叉树_第2张图片

4、特殊的二叉树

  • 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是2^k - 1,则它就是满二叉树。
  • 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树引申出来的。对于深度为K、有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。要注意的是满二叉树是一种特殊的完全二叉树。
    树与二叉树_第3张图片

5、二叉树的性质

树与二叉树_第4张图片

6、二叉树的存储结构

(1)顺序存储

二叉树的顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实使用中只有堆才会使用数组来存储,二叉树顺序存储在物理存储上是一个数组,在逻辑上则是一颗二叉树。

(2)顺序存储结构图

树与二叉树_第5张图片

(3)链式存储

二叉树的链式存储结构是指用链表来表示一棵二叉树,即用链来表示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点的左孩子和右孩子所在的链结点的存储地址。

(4)链式存储结构图(二叉链)

树与二叉树_第6张图片

三、二叉树的遍历

1、概念与三种遍历方式

  • 二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。
  • 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
  • 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
  • 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

2、前序遍历的代码

void BinaryTreePrevOrder(BTNode* root)
{
	//可以不加下面的if语句及其内容,只是节点为空时不打印东西而已
	if (root == NULL)
	{
		printf("# ");
		return;
	}

	printf("%c ", root->data);
	//可以用下面的式子替代上面的式子
	//putchar(root->_data);

	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}

中序与后续的遍历与前序遍历的代码差不多,只需要修改一下最后面的三个式子而已。
函数的参数参见下方二叉树创建函数中的root,将 BTNode* 类型的root作为实参传递即可。

四、二叉树创建函数的实现

1、二叉树的链式存储结构体

typedef char BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data; 
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

2、二叉树创建前的赋值

int main()
{
	char str[] = "ABD##E#H##CF##G##";
	int i = 0;
	int size = sizeof(str) / sizeof(str[0]);
	BTNode* root = BinaryTreeCreate(str, size, &i);
}

3、代码

BTNode *BinaryTreeCreate(BTDataType * src, int n, int* pi)
{
	if (*pi >= n || src[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}

	BTNode * cur = (BTNode *)malloc(sizeof(BTNode));
	cur->data = src[*pi];
	(*pi)++;

	cur->left = BinaryTreeCreate(src, n, pi);
	cur->right = BinaryTreeCreate(src, n, pi);

	return cur;
}

4、实现原理

  • 采用前序遍历的方式创建二叉树,先对根节点进行赋值再对左子树进行遍历与赋值,然后再对右子树进行遍历与赋值,最后返回cur指针。
  • 当外部的字符串在指定位置的值为’#'时,就直接返回NULL,即不对指定位置的二叉树节点赋值。
  • *pi >= n 是当遍历的遍数比外部的字符串的长度还长时,就不再执行函数的递归了。

五、二叉树销毁函数的实现

1、代码

void BinaryTreeDestory(BTNode** root)
{
	if (*root)
	{
		BinaryTreeDestory(&(*root)->left);
		BinaryTreeDestory(&(*root)->right);
		free(*root);
        *root = NULL;
	}
}

2、实现原理

  • 采用递归和后续遍历的方法对二叉树进行遍历并对根节点进行释放,否则,如果根节点在前面进行释放,则会找不到左右子树根节点的地址。
  • (*root)->_left 是一级指针,所以用二级指针作为形参,否则将无法对节点进行修改。

3、注意点

进行函数调用的参数需为 &(*root)->_left ,不能去掉’&',否则,即传的实参是 (*root)->left 时,传的只是一级指针,而下一个递归函数的形参只是这个一级指针的临时拷贝,编译器会在内存中另外开辟一块空间进行存储,而对这个形参进行修改,不会影响到实参,即节点本身。

六、二叉树的层序遍历

1、概念

设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

2、结构图

树与二叉树_第7张图片

3、代码

void BinaryTreeLevelOrder(BTNode* root)
{
	Queue qu;
	BTNode* cur;

	QueueInit(&qu);

	QueuePush(&qu, root);

	while (!QueueEmpty(&qu))
	{
		cur = QueueFront(&qu);

		putchar(cur->data);
		
		if (cur->left)
		{
			QueuePush(&qu, cur->left);
		}

		if (cur->right)
		{
			QueuePush(&qu, cur->right);
		}

		QueuePop(&qu);
	}

	QueueDestroy(&qu);
}

4、实现原理

  • 因为C语言没有队列的库,而实现二叉树的层序遍历需要队列,所以得自己写一个队列。实现队列的函数参见栈与队列
  • 首先入队列一个元素,保证队列中有元素,然后进入循环,取出队列头部的元素,再将这个头部元素的左右子节点入到队列中,最后出队列的头部元素,然后第一次循环结束,再次进入循环,此时,队列头部的元素变成的二叉树根的左子节点,下一次循环,则变为二叉树根的右子节点,即二叉树的第二层元素,以此类推,层序遍历得以实现。
  • 最后,层序遍历完,记得对队列进行销毁的操作。

5、实现原理图

树与二叉树_第8张图片

6、注意点

  • 循环是对队列判空而不是对接收从队列中取出的头元素判空。
  • 循环中,要对左右指针进行判空才能入队列,否则跳过入队列的操作,不然入了NULL,当cur走到这个NULL时,NULL->left或NULL->right将是非法的。

本文到这里就结束了,如有错误或者不清楚的地方欢迎评论或者私信
如果觉得写得不错,请务必一键三连

你可能感兴趣的:(数据结构初阶,数据结构,c语言,visual,studio)