【数据结构】树(tree)

树的简介

一、树的基本概念

1.树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
现实的树
逻辑树
2.除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i
<= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继
因此,树是递归定义的。
注意:子树之间不能有交集,否则不叫树
非树?

二、树的术语

树的术语

节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为6

叶节点或终端节点:度为0的节点称为叶节点; 如上图:B、C、H、I…等节点为叶节点

非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G…等节点为分支节点

双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点

孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点

兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点

树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6

节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;

树的高度或深度:树中节点的最大层次; 如上图:树的高度为4

堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:H、I互为兄弟节点

节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先

子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙

森林:由m(m>0)棵互不相交的树的集合称为森林;

三、树的应用

树的应用

二叉树

一、二叉树的概念

1.二叉树的定义

二叉树是每个结点至多有两个子树的树,二叉树是n (n≥0) 个结点的有限集合:
(1)或者为空二叉树,即n=0。
(2)或者由一个根结点和两个互不相交的被称为根的左子树和右子树组成。左子树和右子树又分别是一棵二叉树。
二叉树
另:对于任意的二叉树都是由以下几种情况复合而成的:
二叉树的情况

2.特殊的二叉树

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

3. 二叉树的性质

(1)若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2i-1个结点.
(2)若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2h-1
(3)对任何一棵二叉树, 如果度为0其叶结点个数为n0, 度为2的分支结点个数为 n2,则有n0= n2+1
(4)若规定根节点的层数为1,具有n个结点的满二叉树的深度h=log2(n+1)
(5)如果将按照具有n个结点的完全二叉树从上至下从左至右的顺序放入数组,对于下标为i的结点有:
a.若i>0,i位置节点的双亲下标为:(i-1)/2;
i=0,i为根节点编号,无双亲节点

b.左孩子下标为:2i+1,若2i+1>n,则没有左孩子
b.右孩子下标为:2i+2,若2i+2>n,则没有右孩子

二、二叉树的顺序结构实现——堆

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

1.堆的概念

堆就是一颗按照父节点大于两个子节点或者父节点小于两个子节点的方式储存在数组中的完全二叉树;
堆的性质
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。

2.堆的实现

堆的基本功能
// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);
//堆打印
void HeapPrint(Heap* hp);
实现
//堆的实现
#include"Heap.h"
//typedef int HPDataType;
//typedef struct Heap
//{
//	HPDataType* a;
//	int size;
//	int capacity;
//}Heap;

void Swap(HPDataType* a, HPDataType* b)
{
	HPDataType tmp = *a;
	*a = *b;
	*b = tmp;
}
//向上调整
void AdjustUp(HPDataType* a, int child)//大堆
{
	assert(a);
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[parent] < a[child])
		{
			Swap(a + parent, a + child);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			break;
	}
	
}
// 向下调整
 void AdjustDown(HPDataType* a,int n, int parent)//大堆
{
	 assert(a);
	 int child = parent * 2 + 1;
	 while (child < n)
	 {
		 //选大的那个孩子
		 if (a[child] < a[child + 1] && child + 1 < n)
			 child++;

		 if (a[parent] < a[child])
		 {
			 Swap(a + parent, a + child);
			 parent = child;
			 child = parent * 2 + 1;
		 }
		 else
			 break;
	 }
}
// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n)
{
	assert(hp);
	if (a == NULL)//如果a为空,则建立空栈
	{
		hp->a = NULL;
		hp->capacity = hp->size = 0;
		return;
	}
	//以输入数组为基础建立栈
	hp->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	if (hp->a == NULL)//要记得判断是否为空
	{
		perror("malloc fail");
		exit(-1);
	}
	hp->capacity = hp->size = n;
	memcpy(hp->a, a, sizeof(HPDataType) * n);//memcpy拷贝的是字节数,所以要记得算一下

	int i = 0;
	//向上调整建堆
	while (i < hp->size)
	{
		AdjustUp(hp->a, i++);
	}
}
// 堆的销毁
void HeapDestory(Heap* hp)
{
	assert(hp);
	free(hp->a);
	hp->a = NULL;
	hp->capacity = hp->size = 0;
}
// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
	//空间不够要扩容
	if (hp->capacity == hp->size)
	{
		int newcpcity = hp->capacity == 0 ? 4 : hp->capacity*2;
		HPDataType *tmp = (HPDataType*)realloc(hp->a, newcpcity*sizeof(HPDataType));
		if (tmp == NULL)
		{
			perror("relloc fail");
			exit(-1);
		}
		hp->a = tmp;
		hp->capacity = newcpcity;
	}
	hp->a[hp->size] = x;
	
	AdjustUp(hp->a, hp->size);

	hp->size++;
}
// 堆的删除
void HeapPop(Heap* hp)
{
	assert(hp);
	assert(hp->size);
	Swap(&hp->a[0], &hp->a[hp->size - 1]);
	hp->size--;
	AdjustDown(hp->a, hp->size, 0);
}
// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
	assert(hp);
	assert(hp->a);
	return hp->a[0];
}
// 堆的数据个数
int HeapSize(Heap* hp)
{
	assert(hp);
	return hp->size;
}
// 堆的判空
int HeapEmpty(Heap* hp)
{
	assert(hp);
	return hp->size == 0;
}
void HeapPrint(Heap* hp)
{
	assert(hp);
	assert(hp->size);
	for (int i = 0; i < hp->size; i++)
	{
		printf("%d ", hp->a[i]);
	}
	printf("\n");
}

三、二叉树链式结构的实现

链式结构大部分的实现都是通过递归来进行,主要思想是将一个大问题分治成相似小问题。

1.主要功能

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);

2.具体实现

#include"BinaryTree.h"
#include"Queue.h"
//typedef char BTDataType;
//
//typedef struct BinaryTreeNode
//{
//	BTDataType _data;
//	struct BinaryTreeNode* _left;
//	struct BinaryTreeNode* _right;
//}BTNode;
BTNode* BuyNewBTNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->_data = x;
	newnode->_left = NULL;
	newnode->_right = NULL;
	return newnode;
}
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
	assert(a);
	if (a[*pi] == '#' || n <= *pi)
	{
		(*pi)++;//步进
		return NULL;
	}

	BTNode* root = BuyNewBTNode(a[*pi]);
	(*pi)++;//步进

	root->_left = BinaryTreeCreate(a , n, pi);
	root->_right = BinaryTreeCreate(a , n, pi);
	return root;
}
void Destory(BTNode* root)
{
	if (root == NULL)
		return;
	Destory(root->_left);
	Destory(root->_right);
	free(root);
}
// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	assert(root);
	Destory(*root);
	*root = NULL;
}
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	int left = BinaryTreeSize(root->_left);
	int right = BinaryTreeSize(root->_right);
	return 1 + left + right;
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	if (root->_left == NULL && root->_right == NULL)
		return 1;
	int left = BinaryTreeLeafSize(root->_left);
	int right = BinaryTreeLeafSize(root->_right);
	return left + right;
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	assert(k > 0);
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->_left, k - 1) +
		BinaryTreeLevelKSize(root->_right, k - 1);
}
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	if (root->_data == x)
		return root;
	BTNode* left = BinaryTreeFind(root->_left, x);
	BTNode* right = BinaryTreeFind(root->_right, x);
	if (left != NULL)
		return left;
	return right;
}
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%c ", root->_data);
	BinaryTreePrevOrder(root->_left);
	BinaryTreePrevOrder(root->_right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	BinaryTreeInOrder(root->_left);
	printf("%c ", root->_data);
	BinaryTreeInOrder(root->_right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	BinaryTreePostOrder(root->_left);
	BinaryTreePostOrder(root->_right);
	printf("%c ", root->_data);
}
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue Q;
	QueueInit(&Q);

	if (root)
		QueuePush(&Q, root);
	while (!QueueEmpty(&Q))
	{
		BTNode* front = QueueFront(&Q);
		printf("%c ", front->_data);

		if(front->_left)
			QueuePush(&Q, front->_left);
		if(front->_right)
			QueuePush(&Q, front->_right);
		QueuePop(&Q);
	}
	QueueDestroy(&Q);
	printf("\n");
}
// 判断二叉树是否是完全二叉树

递归
//int BinaryTreeComplete(BTNode* root)
//{
//	if (root == NULL || (root->_left == NULL && root->_right == NULL))
//		return 1;
//	if (root->_left != NULL && root->_right == NULL)
//		return 0;
//	if (root->_left == NULL && root->_right != NULL)
//		return 0;
//
//	return BinaryTreeComplete(root->_left) && BinaryTreeComplete(root->_right);
//}

//非递归
int BinaryTreeComplete(BTNode* root)
{
	Queue Q;
	QueueInit(&Q);

	if (root)
		QueuePush(&Q, root);
	//找到第一个空节点,退出
	while (!QueueEmpty(&Q))
	{
		BTNode* front = QueueFront(&Q);
		if (front == NULL)
			break;
		QueuePush(&Q, front->_left);
		QueuePush(&Q, front->_right);
		QueuePop(&Q);
	}
	//
	while (!QueueEmpty(&Q))
	{
		BTNode* front = QueueFront(&Q);
		if (front)
		{
			return 0;
			QueueDestroy(&Q);
		}
		QueuePop(&Q);
	}
	QueueDestroy(&Q);
	return 1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值