树和二叉树

树和二叉树

一、基本概念

根结点: 树的最顶层的结点,一棵树有且仅有一个。

子树: 一棵树除根结点外,剩余的是若干个互不相交的有限集,每一个集合本身又是棵树,称为根的子树。

结点的度: 树的结点包含一个数据元素及若干个指向其子树的分支,结点拥有的子树数称为结点的度。

叶子结点: 度为0的结点。

分支结点: 结构的度不为0,被称为分支结点或非终端结点,也被称为内部结点。

树的度: 是指树内各结点度的最大值。

密度: 指的是一棵树中,所有结点的总数。

孩子、双亲、兄弟、祖先、子孙: 结点的子树称为该结点的孩子,而该结点是孩子结点的双亲,拥有共同双亲的结点互为兄弟,从双亲结点往上,直到根结点都称为孩子结点的祖先结点,以某结点为根的子树中的任一结点都被称为该结点的子孙。

层数、深度、高度: 从根结点开始定义,根为第一层、根的孩子为第二层依次类推,树中结点的最大层数被称为树的深度或高度,双亲在同一层的结点互为堂兄弟。

有序树和无序树: 将树中结点的各子树看成从左到右是有序次,即不能交换(顺序有意义,表达一些含义),则称该树为有序树,否则称为无序树。

森林: 若干个棵互不相交的树的集合称为森林,对树中每个结点而言,其子树集合就是森林。

二、二叉树
二叉树:

​ 是一种特殊的树型结构,也就是每个结点最多有两棵子树(二叉树中不存在度大于2的结点),并且二叉树的子树有左右之分,顺序不能颠倒。

满二叉树:

​ 若一棵树的层数为k,它总结点数是(2^k)-1,则这棵树就是满二叉树。

完全二叉树:

​ 若一棵树的层数为k,它前k-1层的总结点数是2^(k-1)-1(前k-1层是满二叉树),第k层的结点按照从左往右的顺序排列,则这棵树就是完全二叉树。

二叉树的性质:
性质1:

​ 在二叉树的第i层上,最多有2^(i-1) [i=1,2,3…n]个结点。

性质2:

​ 深度为k的二叉树,最多有(2^k)-1[k=1,2,3…n]个节点。

性质3:

​ 对于任何一棵二叉树,如果叶子结点的数量为n0,度为2结点的数量为n2,则n0=n2+1;

性质4:

具有n个结点的完全二叉树的高度为⌊ log2n ⌋+1。

性质5:

​ 如有一个n个结点的完全二叉树,结点按照从上到下从左到右的顺序排序为1~n。

​ 1、i > 1时,i/2就是它的双亲结点。

​ 2、i*2是i的左子树,当i*2>n时,则i没有左子树。

​ 3、2*i+1是i的右子树,2*i+1>n时,则i没有右子树。

三、二叉树的实现
1、顺序存储

由于顺序存储是根据元素的相对位置来确定,所以需要将二叉树补为完全二叉树,可以用一些特殊值来表示空白位置,然后就可以通过性质5的公式来计算对应位置,要注意下标和性质中序号的转换。

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>
#include<math.h>
#define LEFT_INDEX(index) ((((index)+1)*2)-1)
#define RIGHT_INDEX(index) (((index)+1)*2)
#define PARENT_INDEX(index) (((index)+1)/2-1)

typedef struct TreeArray
{
	char* arr;
	size_t cnt;
}TreeArray;

//str是完全二叉树的层序遍历结果
TreeArray* create_tree(const char* str)
{
	TreeArray* tree=malloc(sizeof(TreeArray));
	tree->cnt=strlen(str);
	tree->arr=malloc(tree->cnt);
	//只拷贝cnt个字符 不一定以'\0'结束
	strncpy(tree->arr,str,tree->cnt);
	return tree;
}

//销毁
void destroy_tree(TreeArray* tree)
{
	free(tree->arr);
	free(tree);
}

//访问左子树
char _left_tree(TreeArray* tree,int index)
{
	//	index是下标
	int li=LEFT_INDEX(index);
	if(li>=tree->cnt)
		return '#';

	return tree->arr[li];
}
char left_tree(TreeArray* tree,char val)
{
	for(int i=0;i<tree->cnt;i++)
	{
		if(tree->arr[i]==val)
			return _left_tree(tree,i);
	}
	return '#';
}
//访问右子树
char _right_tree(TreeArray* tree,int index)
{
	int ri=RIGHT_INDEX(index);
	if(ri>=tree->cnt)
		return '#';
	return tree->arr[ri];
}
char right_tree(TreeArray* tree,char val)
{
	for(int i=0;i<tree->cnt;i++)
	{
		if(tree->arr[i]==val)
			return _right_tree(tree,i);
	}
	return '#';
}

//访问双亲
char _parents_tree(TreeArray* tree,int index)
{
   	int pi=PARENT_INDEX(index);
	if(index<0)
		return '#';
	return tree->arr[pi];    
}
char parents_tree(TreeArray* tree,char val)
{
	for(int i=0;i<tree->cnt;i++)
	{
		if(tree->arr[i]==val)
			return _parents_tree(tree,i);
	}
	return '#';
}

//计算高度
size_t height_tree(TreeArray* tree)
{
	for(int h=1;h<tree->cnt;h++)
	{
		if(pow(2,h)-1 >= tree->cnt)
			return h;
	}
}
//计算密度
size_t density_tree(TreeArray* tree)
{
	size_t count=0;
	for(int i=0;i<tree->cnt;i++)
	{
		count+=tree->arr[i]!='#';
	}
	return count;
}
//前序遍历
void _pre_tree(TreeArray* tree,int index)
{
	if(0>index||index>=tree->cnt)
		return;
	if('#' == tree->arr[index])
		return;
	printf("%c ",tree->arr[index]);
	_pre_tree(tree,LEFT_INDEX(index));
	_pre_tree(tree,RIGHT_INDEX(index));
}
void pre_tree(TreeArray* tree)
{
	_pre_tree(tree,0);
	printf("\n");
}
//中序遍历
void _mid_tree(TreeArray* tree,int index)
{
	if(0>index||index>=tree->cnt)
		return;
	if('#' == tree->arr[index])
		return;
	_mid_tree(tree,LEFT_INDEX(index));
	printf("%c ",tree->arr[index]);
	_mid_tree(tree,RIGHT_INDEX(index));
}
void mid_tree(TreeArray* tree)
{
	_mid_tree(tree,0);
	printf("\n");
}
//后序遍历
void _post_tree(TreeArray* tree,int index)
{
	if(0>index||index>=tree->cnt)
		return;
	if('#' == tree->arr[index])
		return;
	_post_tree(tree,LEFT_INDEX(index));
	_post_tree(tree,RIGHT_INDEX(index));
	printf("%c ",tree->arr[index]);
}
void post_tree(TreeArray* tree)
{
	_post_tree(tree,0);
	printf("\n");
}

int main(int argc,const char* argv[])
{
	const char* str="HCDGF##Z";	
	TreeArray* tree=create_tree(str);
	printf("%c\n",right_tree(tree,'D'));
	printf("%c\n",parents_tree(tree,'D'));
	printf("%d\n",height_tree(tree));
	printf("%d\n",density_tree(tree));
	pre_tree(tree);
	mid_tree(tree);
	post_tree(tree);
	return 0;
}
2、链式存储

有两种创建链式二叉树的方式:

方式一: 需要给每个度不为2的结点补充一些空白子结点,使树中除了空白结点其它结点的度全为2,然后以前序的方式遍历二叉树,然后以遍历的结果创建二叉树。

方式二: 不需要对二叉做任何改变,以前序+中序或中序+后序的遍历结果创建二叉树。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct TreeNode
{
	char val;
	struct TreeNode* left;
	struct TreeNode* right;
}TreeNode;

TreeNode* create_node(char val)
{
	TreeNode* node = malloc(sizeof(TreeNode));
	node->val = val;
	node->left = NULL;
	node->right = NULL;
	return node;
}

TreeNode* _create_tree(const char* str,int* ip)
{ 
	if('#' == str[*ip])
		return NULL;

	TreeNode* node = create_node(str[*ip]);
	*ip += 1;
	node->left = _create_tree(str,ip);
	*ip += 1;
	node->right = _create_tree(str,ip);
	return node;
}

TreeNode* create_tree(const char* str)
{
	int index = 0;
	return _create_tree(str,&index);
}
//前序  中序 后序类似 只需要改变下位置 
void pre_tree(TreeNode* tree)
{
	if(NULL == tree)
		return;
	printf("%c ",tree->val);
	pre_tree(tree->left);
	pre_tree(tree->right);
}

int main(int argc,const char* argv[])
{
	TreeNode* tree = create_tree("HCGZ###F##D##");
	pre_tree(tree);
	return 0;
}

以中序+后序为例

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>

typedef struct TreeNode
{
	char val;
	struct TreeNode* left;
	struct TreeNode* right;
}TreeNode;

TreeNode* create_node(char val)
{
	TreeNode* node=malloc(sizeof(TreeNode));
	node->val=val;
	node->left=NULL;
	node->right=NULL;
	return node;
}

TreeNode* post_mid_tree(char* post,char* mid,size_t len)
{
	if(0>=len)
		return NULL;
	TreeNode* node=create_node(post[len-1]);
	int ri=0;
	while(ri<len&& mid[ri]!=post[len-1])
		ri++;

	node->left=post_mid_tree(post,mid,ri);
	node->right=post_mid_tree(post+ri,mid+ri+1,len-ri-1);
}
void pre_tree(TreeNode* tree)
{
	if(NULL==tree)
		return;
	printf("%c ",tree->val);
	pre_tree(tree->left);
	pre_tree(tree->right);
}
int main(int argc,const char* argv[])
{
	char post[] = "74258631";
    char mid[] = "47215386";
	TreeNode* tree = post_mid_tree(post,mid,strlen(post));
	pre_tree(tree);
	return 0;
}
四、二叉搜索树

二叉查找树(Binary Search Tree),又称二叉搜索树,二叉排序树。它具有如下性质:

1、对于任意节点,其左子树中的值都小于该节点的值,右子树中的值都大于该节点的值。

2、左子树和右子树也都是二叉搜索树。

对于这样有规律的树,我们就可以进行快速的插入删除查找等操作了。

插入:

​ 向二叉搜索树中插入一个新的节点时,需要从根节点开始比较节点的值,并根据比较结果选择左子树或右子树继续进行比较,直到找到一个空的位置插入新节点。

删除:
删除节点时,首先需要找到待删除的节点。如果待删除的节点没有子节点,可以直接删除即可。如果待删除的节点有一个子节点,可以用子节点替换待删除的节点。如果待删除的节点有两个子节点,可以找到其右子树中的最小节点,即右子树中的最左节点,将其值复制到待删除节点,并删除该最小节点。

查找:
查找操作在二叉搜索树中非常高效。从根节点开始,若目标值等于当前节点的值,则找到了目标节点。若目标值小于当前节点的值,则继续在左子树中查找,若目标值大于当前节点的值,则继续在右子树中查找,直到找到目标节点或遍历到空节点。

缺点:极端情况下会影响树的形状,如插入的数据有序,他就会呈单支状分布,进而降低效率。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct TreeNode
{
	int val;
	struct TreeNode* left;
	struct TreeNode* right;
}TreeNode;

TreeNode* create_node(int val)
{
	TreeNode* node = malloc(sizeof(TreeNode));
	node->val = val;
	node->left = NULL;
	node->right = NULL;
	return node;
}

void destroy_tree(TreeNode* tree)
{
	if(NULL == tree)
		return;

	destroy_tree(tree->right);
	destroy_tree(tree->left);
	printf("%d ",tree->val);
	free(tree);
}

/*
void insert_tree(TreeNode** tree,int val)
{
	if(NULL == *tree)
	{
		*tree = create_node(val);
		return;
	}

	if(val < (*tree)->val)
		insert_tree(&(*tree)->left,val);
	else
		insert_tree(&(*tree)->right,val);
}
*/
TreeNode* _insert_tree(TreeNode* tree,TreeNode* node)
{
	if(NULL == node)
		return tree;
	if(NULL == tree)
		return node;
	//小于根节点的值,成为左子树
	if(node->val < tree->val)
		tree->left = _insert_tree(tree->left,node);
	else
		tree->right = _insert_tree(tree->right,node);
	return tree;
}

TreeNode* insert_tree(TreeNode* tree,int val)
{
	return _insert_tree(tree,create_node(val));
}

TreeNode* query_tree(TreeNode* tree,int key)
{
	if(NULL == tree)
		return NULL;

	if(key < tree->val)
		return query_tree(tree->left,key);
	if(key > tree->val)
		return query_tree(tree->right,key);
	return tree;
}

void mid_tree(TreeNode* tree)
{
	if(NULL == tree)
		return;

	mid_tree(tree->left);
	printf("%d ",tree->val);
	mid_tree(tree->right);
}

TreeNode* delete_tree(TreeNode* tree,int key)
{
	if(NULL == tree)
		return NULL;

	if(key < tree->val)
	{
		tree->left = delete_tree(tree->left,key);
		return tree;
	}
	if(key > tree->val)
	{
		tree->right = delete_tree(tree->right,key);
		return tree;
	}

	tree->left = _insert_tree(tree->left,tree->right);
	TreeNode* tmp = tree->left;
	free(tree);
	return tmp;

	/*
	if(NULL == tree)
		return NULL;

	if(key < tree->val)
		tree->left = delete_tree(tree->left,key);
	if(key > tree->val)
		tree->left = delete_tree(tree->right,key);

	if(NULL == tree->left && NULL == tree->right)
	{
		free(tree);
		return NULL;
	}
	if(NULL == tree->right)
	{
		TreeNode* tmp = tree->left;
		free(tree);
		return tmp;
	}
	if(NULL == tree->left)
	{
		TreeNode* tmp = tree->right;
		free(tree);
		return tmp;
	}

	TreeNode* min = tree->right;
	while(NULL != min->left)
		min = min->left;

	int tmp = tree->val;
	tree->val = min->val;
	min->val = tmp;

	tree->right = delete_tree(min,key);
	return tree;
	*/
}

int main(int argc,const char* argv[])
{
	TreeNode* tree = NULL;
	int arr[10];
	for(int i=0; i<10; i++)
	{
		arr[i] = rand()%100;
		tree = insert_tree(tree,arr[i]);
		printf("%d ",arr[i]);
	}
	printf("\n");
	/*
	//mid_tree(tree);
	printf("\n");
	for(int i=0; i<10; i++)
	{
		tree = delete_tree(tree,arr[i]);
		mid_tree(tree);
		printf(" del %d\n",arr[i]);
	}
	*/
	destroy_tree(tree);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值