数据结构-二叉树和堆的实现

树的概念和结构

概念

树是一种非线性的数据结构,它是由n(n>=0)个有限节点组成一个具有层次关系的集合。
把它叫做树是因为它看起来像一颗倒挂的树,也就是说根是朝上的,叶子朝下。
1.树有一个特殊节点,称为根节点,根节点没有前驱节点。
2.除了根节点外,其余节点被分成M(M>0)个互不相交的集合T1、T2、.....等,其中每个集合又是一棵结构与树类似的子树。
每颗子树的根节点有且只有一个前驱,可以有0个或多个后继。
树是递归定义的。

结构

在这里插入图片描述

注意:1.子树能不能相交的
	  2.除了根节点外,每个节点有且只有一个父节点
	  3.一颗N个节点的树有N-1条边

在这里插入图片描述

树的其他概念名词

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

树的孩子兄弟表示法

typedef int DataType;
typedef struct Node
{
	struct Node* firstChild1;			//第一个孩子节点
	struct Node* pNextBrother;			//孩子的兄弟节点
	DataType data;
}Node;

在这里插入图片描述

树的实际应用

计算机中的文件系统都是用的树型结构,Windows、Linux等

二叉树的概念和结构

概念

一颗二叉树是节点的一个有限集合,该集合或者为空,或者是由一个根节点加上两颗分别称为左子树和右子树的二叉树组成。
特点:1.每个节点最多有两棵子树,即二叉树不存在度大于2的节点。
	 2.二叉树的子树有左右之分,其子树的次序不能颠倒。

上面的树就是二叉树,二叉树也可以是但分支的,比如下图中都是二叉树

在这里插入图片描述

特殊的二叉树

1.满二叉树:一个二叉树,如果每一层的节点都达到最大值,则这个二叉树就是满二叉树。
如果一个满二叉树层数为k,且节点总数为(2^k)-1个,那么它就是满二叉树
2.完全二叉树:完全二叉树是由满二叉树引出来的,对于深度为k,节点为n的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中编号从1至n的节点一一对应时称之为完全二叉树。
满二叉树是一种特殊的完全二叉树。

在这里插入图片描述

二叉树的性质

1.若规定根节点的层数为1,则一颗非空二叉树的第i层最多有2^(i-1)个节点
2.若规定根节点的层数为1,则深度为h的二叉树的最大节点数是2^h-13.对任何一颗二叉树,如果度为0的叶子节点个数为n0,度为2的分支节点为n2,则有n0=n2+1。(没有孩子的节点比有两个孩子的节点个数多一)
4.若规定根节点的层数为1,具有n个节点的满二叉树的深度,h=log2(n+1(2为底,n+1为对数),深度为h,节点n=2^h-1
5.对于具有n个节点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的节点:
若i>0,双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
若2i+1<n,左孩子序号:2i+1,否则无左孩子
若2i+2<n,右孩子序号:2i+2,否则无右孩子

二叉树的存储结构

一种是顺序结构,一种是链式结构
现实中顺序结构一般是用来实现堆的(完全二叉树)

链式结构有:二叉链表和三叉链表,本文先学二叉链表

typedef int BTDataType;
struct BinaryTreeNode
{
	struct BinaryTreeNode* pLeft;		//左孩子
	struct BinaryTreeNode* pRight;		//右孩子
	BTDataType data;					//数据
};

二叉树的顺序结构-堆

普通的二叉树是不适合用数组存储的,因为可能会造成大量的空间浪费。
而完全二叉树更适合使用顺序结构存储。
堆:如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储
在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 012…,则称为
小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆
大堆:树中所有得父亲节点都大于等于孩子
小堆:树中所有得父亲节点都小于等于孩子

在这里插入图片描述

堆的实现

堆的向下调整算法

前提:二叉树的左右子树必须都是小堆或者大堆。
Swap(int* a,int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

AdjustDown(int* a, int parent, int size)
{
	int child = 2*parent + 1;
	while(child<size)
	{
		if(child + 1 < size && a[child] > a[child+1])		//选出小的那个
		{
			child++;
		}
		if(a[parent] > a[child])
		{
			Swap(&a[parent],&a[child]);
			parent = child;
			child = 2*parent + 1;
		}
		else
		{
			break;
		}
	}
}


int main()
{
	int a[] = { 27,15,19,18,28,34,65,49,25,37 };
	int k = sizeof(a) / sizeof(a[0]);
	AdjustDown(a, 0, k);
	for (int i = 0; i < k; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
	return 0;
}

在这里插入图片描述

堆的创建

建堆就是把二叉树分成多个小二叉树,然后分别用向下调整算法把二叉树调成大堆
从右下角的二叉树开始
typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}Heap;

void HeapCreate(Heap* hp, HPDataType* a, int n)
{
	assert(hp);
	hp->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	if (hp->a == NULL)
	{
		perror("HeapCreate malloc fail");
		exit(-1);
	}

	hp->size = hp->capacity = n;
	memcpy(hp->a, a, sizeof(HPDataType)*n);

	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(hp->a, i, n);
	}
}

堆的销毁

void HeapDestroy(Heap* hp)
{
	assert(hp);
	free(hp->a);
	hp->a = NULL;
	hp->size = hp->capacity = 0;
}

堆得插入

//在堆的最后面插入数据,然后在向上调整
void AdjustUp(int* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[parent] > a[child])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
	if (hp->capacity == hp->size)
	{
		hp->capacity *= 2;
		HPDataType* tmp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * hp->capacity);
		if (tmp == NULL)
		{
			perror("HeapPush realloc fail");
			exit(-1);
		}
		hp->a = tmp;
	
	}
	hp->a[hp->size] = x;
	hp->size++;
	AdjustUp(hp->a, hp->size - 1);			//向上调整算法
}

堆的删除

//删除的是堆顶的数据
void HeapPop(Heap* hp)
{
	assert(hp);
	Swap(&hp->a[0], &hp->a[hp->size-1]);
	hp->size--;
	//AdjustDown(hp->a, 0,hp->size);
	HeapSoft(hp);			//堆的排序,后面有
}

堆顶的数据

HPDataType HeapTop(Heap* hp)
{
	assert(hp);
	assert(hp->size > 0);
	return hp->a[0];
}

堆数据的个数

int HeapSize(Heap* hp)
{
	assert(hp);
	return hp->size;
}

判断堆是否为空

int HeapEmpty(Heap* hp)
{
	assert(hp);
	return hp->size;
}

堆排序

对堆进行排序,先建堆,然后把堆顶的数据和最后一位数据调换位置,在对剩下的数据重新建堆
排升序要建大堆,因为如果建小堆,第一次建堆选出最小的数和最后一位换,这时候找次小的,堆的结构被打乱,还要重新建堆,效率太差,建大堆就是把最大的放到最后一位,然后剩下的数进行向下调整
排降序建小堆
void HeapSoft(Heap* hp)
{
	assert(hp);
	for (int i = (hp->size - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(hp->a, i, hp->size);
	}

	int end = hp->size - 1;
	while (end > 0)
	{
		Swap(&hp->a[0], &hp->a[end]);
		AdjustDown(hp->a, 0, end);
		end--;
	}
}

二叉树的链式结构

typedef char BTDataType;
typedef struct BinaryTreeNode
{
	struct BinartTreeNode* leftChild;			//左孩子
	struct BinartTreeNode* rightChild;			//右孩子
	BTDataType data;
}BTNode;

二叉树的实现会在下一章详细写出来

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值