二叉树粗记

目录

树的概念

二叉树的概念

树的定义 

满二叉树

 ​编辑

完全二叉树

 堆

 堆的实现

链式二叉树

链式二叉树的实现

 层序遍历


树是一种非线性数据结构,顶端是根节点,最底端是叶子结点,像是棵倒立的树

树的概念

子节点:以某节点为根的节点(如,B,C是A的子节点)

叶结点:在树中没有子节点的叫做叶子节点(如,D,E,F,G)

节点的度:一个节点拥有的所有子节点的个数(子孙结点不算)

树的度:在一个树中最大节点的度叫树的度(如,A的度为2)

节点层次:从根开始,根为一,根的子节点为第二层

树的高度或深度:树中节点的最大层次(如图为三层)

森林:互不相交的树的集合

子孙:以某节点为根的子树都可以叫做子孙(所有节点都是A节点的子孙)

根节点:没有父节点的节点

节点的祖先:从根节点到祖先这条路径上的所有节点(如A节点是左右节点的祖先)

......

二叉树的概念

二叉树度最大为二,分为左节点和右节点,子树与子树之间不能相交

次序不能颠倒,因此二叉树是有序树

树的定义 

左孩子右兄弟表示法

 

typedef struct BinaryTreeNode
{
	BTDataType val;
	struct BinaryTreeNode* child;
	struct BinaryTreeNode* brothers;
}BTNode;

如图A有左孩子B和右孩子C两个子节点,B又有左孩子D和右孩子E两个子节点,以此类推 

typedef struct BinaryTreeNode
{
	BTDataType val;
	//左节点
	struct BinaryTreeNode* left;
	//右节点
	struct BinaryTreeNode* right;
}BTNode;

满二叉树

 

满二叉树每层的节点都是满的

当满二叉树深度为h层时,总结点个数是2^h-1

完全二叉树

 完全二叉树最后一层可以不满,但是最后一层从左向右是连续的(完全二叉树可以是满二叉树,但是,满二叉树一定不能是完全二叉树)

当完全二叉树的高度为h层时,完全二叉树节点最多2^h-1个,最少2^(h-1)

 堆

此堆非彼堆,这个堆是一种数据结构操作系统中的堆没有关系

这个堆是二叉树的一种存储结构是使用顺序结构数组来存储数据的,但只适合完全二叉树,因为使用堆这个结构其实就是用下标来访问数据,而其他二叉树由于特殊或是其特可能导致有的地方没有数据,从而导致空间的浪费

 堆有两种结构

大堆:任何一个父节点都大于等于子节点

小堆:任何一个父节点都要小于子节点

堆在内存中的真实存储方式是物理结构,如上图数组

而图中二叉树就是我们想想出来的逻辑结构

 左孩子=父节点*2+1

右孩子=父节点*2+2

父节点=孩子-1/2

 堆的实现

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#define HPcapacity 4
typedef int HPDatatype;
//数组结构
typedef struct Heap
{
	HPDatatype* a;
	int size;
	int capacity;
}HP;
//初始化
void HeaPInit(HP* php);
//销毁
void HeaPDestroy(HP* php);
//判空
bool HeaPEmpty(HP* php);
//向上调整
void HeaPjustup(HPDatatype* a,int x);
//向下调整
void HeaPjustDown(HPDatatype* a, int n, int parent);
//插入
void HeaPPush(HP* php, HPDatatype x);
//堆的数据个数
int HeaPSize(HP* php);
//堆的删除
void HeaPPop(HP* php);
//返回堆顶数据
HPDatatype HeaPTop(HP* php);
//交换函数
void HeaPSwap(HPDatatype* p1, HPDatatype* p2);
//堆排
void HeapSort(HPDatatype * a, int n);

 

//初始化
void HeaPInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->size = php->capacity = 0;

}
//判空
bool HeaPEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}
//销毁
void HeaPDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->size = php->capacity = 0;
	
}
//插入
void HeaPPush(HP* php, HPDatatype x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		int tmp = 0;
		tmp = php->capacity == 0 ? 4 : php->capacity*2;
		HPDatatype* ptr = (HPDatatype*)realloc(php->a,sizeof(HPDatatype) * tmp);
		if (ptr == NULL)
		{
			perror("malloc fail");
			return;
		}
		php->a = ptr;
		php->capacity = tmp;
	}
	php->a[php->size++] = x;
	HeaPjustup(php->a, php->size - 1);
}
//交换函数
void HeaPSwap(HPDatatype* p1, HPDatatype* p2)
{
	HPDatatype tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
//堆的数据个数
int HeaPSize(HP* php)
{
	assert(php);
	return php->size;
}
//向上调整
void HeaPjustup(HPDatatype* a,int child)
{
	int parent = (child-1) / 2;
	while (child>0)
	{
		if (a[child] > a[parent])
		{
			HeaPSwap(&a[child], &a[parent]);
			//替换父子位置
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
//向下调整(注意使用向下调整左右节点必须是大堆或小堆)
void HeaPjustDown(HPDatatype* a,int n, int parent)
{
	int child = parent * 2 + 1;
	//父节点小于总结点
	while (child < n)
	{
		//选出左右孩子最大或最小的	
		//防止没有右节点
		if (child+1<=n&&a[child] < a[child + 1])
		{
			child++;
		}
		//将大的数向下调整
		if (a[child] > a[parent])
		{
			HeaPSwap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		} 
	}
}
//删除
void HeaPPop(HP* php)
{
	assert(php);
	assert(!HeaPEmpty(php));
	
	//首尾交换删除
	HeaPSwap(&php->a[0], &php->a[php->size - 1]);
	php->size--;
	//向下调整
	HeaPjustDown(php->a, php->size, 0);
}
//返回堆顶元素
HPDatatype HeaPTop(HP* php)
{
	assert(php);
	assert(!HeaPEmpty(php));
	return php->a[0];
}
//堆排序
void HeapSort(HPDatatype* a, int n)
{
	//利用向下排序建堆
	int j = (n - 2) / 2;
	int end = n - 1;
	while (j>=0)
	{
		HeaPjustDown(a, n, j);
		--j;
	}
	while (end>0)
	{
		HeaPSwap(&(a[0]), &(a[end]));
		HeaPjustDown(a, --end, 0);
	}
}

链式二叉树

链式二叉树单纯存储数据是没有意义的

链式二叉数结构的定义 

typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	//左节点
	struct BinaryTreeNode* left;
	//右节点
	struct BinaryTreeNode* right;
}BTNode;

链式二叉树是以递归来实现的

分为前序中序后序层序

前序:先访问根再访问左子树,右子树

 中序:先访问左子树,根,右子树

 

 层序:先访问左子树,右子树,根

层序:使用队列,每次根节点出队列,会将子节点带入队列 

链式二叉树的实现

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef char BTDataType;
//定义一个二叉树的结构
typedef struct BinaryTreeNode
{
	BTDataType val;
	//左节点
	struct BinaryTreeNode* left;
	//右节点
	struct BinaryTreeNode* right;
}BTNode;
typedef struct BinaryTreeNode
{
	BTDataType val;
	struct BinaryTreeNode* child;
	struct BinaryTreeNode* brothers;
}BTNode;

//二叉树的创建
BTNode* BTCreate(BTDataType* a, int* pi);
//二叉树的销毁
void BTDestroy(BTNode* root);
//二叉树的节点个数
int  BTSize(BTNode* root);
//二叉树的叶子结点个数
int BTLeaf(BTNode* root);
//二叉树第k层节点个数
int BTLevelKSize(BTNode* root, int k);
//查找二叉树值为x的节点
BTNode* BTFind(BTNode* root, BTDataType x);

//二叉树前序遍历
void BTPrevOrder(BTNode* root);
//二叉树中序遍历
void BTInOrder(BTNode* root);
//二叉树后序遍历
void BTPostOrder(BTNode* root);
BTNode* BTCreate(BTDataType* a, int* pi)
{
	//if (*pi >= n || *pi + 1 > n)
	//{
	//	return NULL;
	//}
	//BTNode* Node = (BTNode*)malloc(sizeof(BTNode));
	//if (Node == NULL)
	//{
	//	perror("malloc fail");
	//	return NULL;
	//}

	//Node->val = a[*pi];
	//int i = 0;
	//i = (*pi) * 2 + 1;
	printf("%d ", Node->val);

	//Node->left = BTCreate(a, n, &i);
	//i = i + 1;
	//Node->right = BTCreate(a, n, &i);
	//return Node;	
	if (a[*pi] == '#')
	{
		(* pi)++;
		return NULL;
	}
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	if (root == NULL)
	{
		perror("malloc fail");
		return NULL;
	}	
	root->val = a[*pi];
	(* pi)++;
	printf("%c ", root->val);
	root->left=BTCreate(a,  pi);
	root->right=BTCreate(a, pi);
	return root;
}
void BTDestroy(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BTDestroy(root->left);
	BTDestroy(root->right);
	free(root);
	root = NULL;
}
int  BTSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return BTSize(root->left) + BTSize(root->right)+1;
}
int x = 0;
int BTLeaf(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	if (root->left == NULL && root->right==NULL)
		return 1;
	///*if (L + R == 0)
	//{
	//	return 1;
	//}*/
	//int num = L + R;
	return BTLeaf(root->right) + BTLeaf(root->left);
	
}
int BTLevelKSize(BTNode* root, int k)
{
	assert(k);
	if (root == NULL)
	{
		return 0;
	}
	if (k==1)
	{
		return 1;
	}
	return BTLevelKSize(root->left,k-1) + BTLevelKSize(root->right, k - 1) ;
}
BTNode* BTFind(BTNode* root, BTDataType x)
{
	
	if (root == NULL)
	{
		return NULL;
	}
	if (root->val == x)
	{
		return root;
	}
	BTNode* Left = BTFind(root->left,x);
	if (Left)
	{
		return Left;
	}
	BTNode* Right = BTFind(root->right, x);
	if (Right)
	{
		return Right;
	}
	
	return NULL;
}

void BTPrevOrder(BTNode* root)
{
	//前序遍历,根,左,右
	if (root == NULL)
	{
		return NULL;
	}
	printf("%d ", root->val); 
	BTPrevOrder(root->left);
	BTPrevOrder(root->right);
	return NULL;
}
void BTInOrder(BTNode* root)
{
	//中序遍历,左,根,右
	if (root == NULL)
	{
		return NULL;
	}
	BTPrevOrder(root->left);
	printf("%d ", root->val);
	BTPrevOrder(root->right);
	return NULL;
}

void BTPostOrder(BTNode* root)
{
	//后序遍历,左,右,根
	if (root == NULL)
	{
		return NULL;
	}
	BTPostOrder(root->left);
	BTPostOrder(root->right);
	printf("%d ", root->val);
	return NULL;
}

//判断是否为完全二叉树
bool BTComplete(BTNode* root)
{
	//层序遍历
	Queue Q;
	QuInit(&Q);
	if (root)
	QuPush(&Q,root);
	while (!QuEmpty(&Q))
	{
		BTNode* BT =QuFront(&Q);
		QuPop(&Q);
		if (BT==NULL)
		{
			break;
		}
		QuPush(&Q, BT->left);
		QuPush(&Q, BT->right);
	}
	while (!QuEmpty(&Q))
	{
		BTNode* BT = QuFront(&Q);
		QuPop(&Q);
		if (BT)
		{
			QuDestroy(&Q);
			return false;
		}
	}
	QuDestroy(&Q);
	return true;
}

 层序遍历

 void BTLevelOrder(BTNode* root)
{
	//队列实现层序遍历
	Queue Q;
	//队列初始化
	QuInit(&Q);
	//根为真push节点进队列
	if(root)
	QuPush(&Q, root);
	while (!QuEmpty(&Q))
	{
		//取出队头数据
		BTNode* r=QuFront(&Q);
		//删除数据
		QuPop(&Q);
		printf("%d ", r->val);
		//把前一个节点的左右子树带入队列
		if (r->left)
		{
			QuPush(&Q, r->left);
		}
		if (r->right)
		{
			QuPush(&Q, r->right);
		}
	}
	//销毁
	QuDestroy(&Q);
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值