zoukankan      html  css  js  c++  java
  • 树的实现及应用

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

    度:

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

    叶节点:

    树中度为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,左儿子,右儿子,然后根据深搜,每次往下搜步数加一,同步到每个之前未经过的到达的节点,搜到底之后往上返回父节点时步数也加一;题目博客链接

  • 相关阅读:
    二叉树的最大深度
    [CSP-S模拟测试]:飘雪圣域(莫队)
    [CSP-S模拟测试]:玩具(概率DP)
    [CSP-S模拟测试]:梦境(贪心+小根堆)
    [CSP-S模拟测试]:小P的生成树(数学+Kruskal)
    [CSP-S模拟测试]:小P的单调数列(树状数组+DP)
    [CSP-S模拟测试]:小P的2048(模拟)
    [CSP-S模拟测试]:简单的操作(二分图+图的直径)
    [CSP-S模拟测试]:简单的期望(DP)
    [CSP-S模拟测试]:简单的序列(DP)
  • 原文地址:https://www.cnblogs.com/heihuifei/p/8120123.html
Copyright © 2011-2022 走看看