树的实现及应用

树是由一个集合及在该集合上定义的一种层次关系构成的。几个树中的名词:

度:

一个节点的儿子节点个数称为该节点的度,一棵树的度是指该树中节点的最大度数。

叶节点:

树中度为0的节点称为叶节点(终端节点);

内部节点:

树中除根节点的且度不为0的节点称为内部节点;

路径:

如果存在树中的一个节点序列k1,k2,...kj,使得节点ki是ki+1的父节点(1<=i<j),则称该节点序列是树中从节点ki到节点kj的一条路径;

高度:

树的一个节点的高度是指从该节点到各叶节点的最长路径的长度;

深度:

从树根到任一节点n有唯一的一条路径,称这条路径的长度为节点n的深度或层次;

树的遍历:

如图假设为一棵树,其中T1,T2可看成是子树(不一定只有一个节点)

树的遍历就是将树中的每个节点访问一次且仅访问一次;一般有三种遍历方式:

前序遍历:

先访问树根n,然后依次前序遍历T1,T2,T3...,Tk;

中序遍历:

先中序遍历T1,然后访问树根n,接着对T2,T3,Tk进行中序遍历;(如果T2这些不是单个节点,则遍历方式就是一个类似的过程,对以T2为根节点的子树中序遍历)

后序遍历:

先一次对T1,T2,T3,...,Tk进行后序遍历,最后访问树根n;

遍历图解:

通过三种遍历方式遍历上图列表:

前序:A B E F I J C D G H

中序:E B I F J A C G D H

后序:E I J F B C G H D A

思路:在对每一个节点遍历,都是对将其作为根节点的子树进行遍历;

树的表示:

父节点数组表示法:开辟一个fa[]数组,从1到n的编号代表每个节点的编号,其中保存的值就是这个节点的父亲节点编号;

儿子链表示法:每个节点建立一个节点表,各节点的儿子表表头存放于数组header中;

二叉树:

二叉树中每个节点至多有两个儿子,并且有左、右之分,任一节点要么没有儿子,要么只有左儿子,要么只有右儿子,要么有一个左儿子和一个右儿子这四种情况;

二叉树的性质:

1、高度h>=0的二叉树至少有h+1个节点;

2、高度不超过h的二叉树至多有2^(h+1)-1个节点;

3、含有n>=1个节点的二叉树的高度至多为n-1,至少为logn;

典型问题:

具有n个节点的不同二叉树的数目有多少?二叉树是递归定义的,因此易知其递归方程是

Bn=1; n=0;

Bn=Sum(Bi*Bn-i-1) (i=0,1,2,3,...n-1) n>=1;

Bn的通解是Bn=C(2n,n)/(n+1),也就是常见的Catalan数;
Catalan数参考博客:http://blog.csdn.net/mobius_strip/article/details/39229895

满二叉树及近似满二叉树:
满二叉树:

每一层上的节点数都达到最大值;

近似满二叉树:

二叉树中至多只有最下面的两层上节点的度数可以小于2,并且最下面一层的节点都集中在最左边,称之为近似满二叉树;
图析:

二叉树的指针实现代码:
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<malloc.h>
typedef struct node* link;
typedef struct node
{
	link parent;
	int element;
	link lchild;
	link rchild;
}Node;
//typedef struct atree* Tree;
//typedef struct atree
//{
//	link root;
//}Atree;

void print_sorted_tree(link tr)                //中序遍历输出树 ; 
{
	if(tr==NULL)return;
	print_sorted_tree(tr->lchild);
	printf("%d 
",tr->element);
	print_sorted_tree(tr->rchild);
}
link find_min(link tr)                         //寻找tr为根节点的树中的最小值; 
{
	link np=tr;
	if(np==NULL)return NULL;
	while(np->lchild!=NULL)
	{
		np=np->lchild;
	}
	return np;
}
link find_max(link tr)                         //寻找tr为根节点的树中的最大值 ; 
{
	link np=tr;
	if(np==NULL)return NULL;
	while(np->rchild!=NULL)
	{
		np=np->rchild;                        //根据二叉树的定义,右儿子一定比其大,则每次都往右边找值; 
	}
	return np;
}
link find_value(link tr,int value)            //寻找树中是否有value这个值,如果有则,输出其位置(link),否则输出NULL; 
{
	if(tr==NULL)return NULL;
	if(tr->element==value)return tr;
	else if(value<tr->element)return find_value(tr->lchild,value);
	else return find_value(tr->rchild,value);
}
int is_root(link np)                          //判断np节点是否是根节点; 
{
	return (np->parent==NULL);
}
int is_leaf(link np)                          //判断np节点是否是叶节点; 
{
	return (np->lchild==NULL&&np->rchild==NULL);
}
int delete_leaf(link np)                      //np是叶节点时,删除叶节点; 
{
	int element;
	link parent;
	element=np->element;
	parent=np->parent;
	if(!is_root(np))                          //直接将np的父节点的左或右儿子置为NULL即可; 
	{
		if(parent->lchild==np)
		{
			parent->lchild=NULL;
		}
		else parent->rchild=NULL;
	}
	free(np);                                 //将np释放; 
	return element;
}
int delete_node(link np)                     //删除某个节点,里面还需要判断其是否是叶节点跳转到叶节点删除函数; 
{
	link replace;
	int element;
	if(is_leaf(np))return delete_leaf(np);
	else
	{
		replace=(np->lchild!=NULL)?find_max(np->lchild):find_min(np->rchild);             //此时np有儿子,则要么将左边比它小的最大数放到其位置,要么将右边比它大的最小数放到其位置; 
		element=np->element;
		np->element=delete_node(replace);                                                 //即最后通过删除一个叶节点(最大值或最小值),将其值放到删除值节点的位置; 
		return element; 
	}
}
void insert_node_to_nonempty_tree(link tr,link np)                                        //当树非空的时候,通过比较数的大小进行插入; 
{
	if(np->element<=tr->element)
	{
		if(tr->lchild==NULL)
		{
			tr->lchild=np;
			np->parent=tr;
			return;
		}
		else insert_node_to_nonempty_tree(tr->lchild,np);                                 
	}
	else
	{
		if(tr->rchild==NULL)
		{
			tr->rchild=np;
			np->parent=tr;
			return;
		}
		else insert_node_to_nonempty_tree(tr->rchild,np);
	}
}
link insert_value(link tr,int value)                    //将value插入到树中,里面需要判断树是否是空的是否需要跳转到插入非空树函数; 
{
	link np;
	np=(link)malloc(sizeof(Node));
	np->element=value;
	np->parent=np->lchild=np->rchild=NULL;
	if(tr==NULL)tr=np;
	else
	{
		insert_node_to_nonempty_tree(tr,np);
	}
	return tr;
}
int main()
{
	link tr;
	link np;
	int element;
	tr=NULL;
	tr=insert_value(tr,19);
	tr=insert_value(tr,4);
	tr=insert_value(tr,2);
	tr=insert_value(tr,8);
	tr=insert_value(tr,81);
	tr=insert_value(tr,101);
	printf("FirstTree:
");
	print_sorted_tree(tr);
	np=find_value(tr,8);
	if(np!=NULL)
	{
		delete_node(np);
		printf("Deletion:
");
		print_sorted_tree(tr);
	}
	return 0;
}
第六次作业:

1、(一棵树,给一个节点求以其作为根节点的子树的所有value和和value中的最大值,输入时通过给出每个点的父节点存储)这题可能会先认为需要对每个节点存储其儿子节点,那会相对复杂一些,可以通过逆向的存储父节点,然后从后往前计算value和就可以解决;题目博客链接

2、(一棵二叉树,每从一个节点到另一个节点步数就加一,求第一次到指定节点的步数)可以通过深搜的思路,每个节点有到该点的步数step,是否访问vis,当前权值val,左儿子,右儿子,然后根据深搜,每次往下搜步数加一,同步到每个之前未经过的到达的节点,搜到底之后往上返回父节点时步数也加一;题目博客链接

原文地址:https://www.cnblogs.com/heihuifei/p/8120123.html