zoukankan      html  css  js  c++  java
  • 3.9重建二叉树(各种方案的分析比较及扩展问题的分析)

    1、二叉树遍历分析

            二叉树的遍历有四种,先序、中序、后序和层序,其中,中序遍历配合其中任何一种遍历的结果都可以重建二叉树,先序和后序配合却无法重建二叉树。本人觉得编程之美书上代码清单3-12给出的代码不一定是最好的,代码冗长,复杂。在此给出自己的尝试和结果。

    2、先序+中序重建二叉树

            先给出一个子函数,检验字符search_char是否在字符串*s的区间[pbeg, pend],也是是否能构建二叉树的判断依据。代码如下:

    bool charisexist(char search_char,char *s,int pbeg,int pend,int &loc)
    {
    	string temp=s;
    	loc=temp.find(search_char);
    	if (loc>pend||loc<pbeg)
             return false;
    	else
    	 return true;
    }

    2.1、方案一

            以先序遍历的结果为顺序一个个字符轮流搜索,重建树,beg,end是所找节点子树在中序遍历的范围。若beg>end,说明为空,返回。

            DLR为先序遍历结果,LDR为中序遍历结果,offset为对DLR检测的下标。[beg, end]为LDR的有效区间,root为根节点,flag判断是否建树成功。flag的初始值为ture,执行完下面的程序后,若flag的值仍为ture,则说明建树成功,否则建树失败,说明所给的结果格式不合法。

    int num=0;
    void searchbuild(char *DLR,int &offset,char *LDR,int beg,int end,node *&root,bool &flag)//方案一,以DLR一个个字符轮流进行
    {
    	int loc=0;
    	num++;//统计递归进入次数
    	if (beg>end)//递归退出条件
    	{
    		root=NULL;//-----------------------此处是关键,注意啊
    		return;
    	}
    	if (charisexist(*(DLR+offset),LDR,beg,end,loc))//根节点的location已存入loc中了	每次比会更新loc的值
    	{
    		node *temp=new node;
    		temp->value=*(DLR+offset++);
    		root=temp;
    	}
    	else
    	{
                    flag=faulse;
    		return;
    	}
    		searchbuild(DLR,offset,LDR,beg,loc-1,(root->pleft));//递归过程及其回溯过程
    		searchbuild(DLR,offset,LDR,loc+1,end,(root->pright));
    }

    运行结果为:

    2.2、方案一的优化

            当beg==end时,beg所指的节点已经是叶节点,此处将其左右指针赋NULL后return,否则程序将进行下一层遍历后return,增加了遍历次数,增加了时间。

    void searchbuild(char *DLR,int &offset,char *LDR,int beg,int end,node *&rootd,bool &flag)//方案一,以DLR字符的轮流进行
    {
    	int loc=0;
    	num++;//统计递归进入次数
    	if (num==0)//格式初步检查,其实可以不要的啦
    	{
    		if (sizeof(DLR)!=sizeof(LDR)||!DLR||!LDR)
    		{
    			flag=faulse;
    			return;
    		}
    	}
    	if (beg>end)//递归退出条件
    	{
    		root=NULL;//-----------------------此处是关键,注意啊
    		return;
    	}
    	if (charisexist(*(DLR+offset),LDR,beg,end,loc))//根节点的location已存入loc中了	每次比会更新loc的值
    	{
    		node *temp=new node;
    		temp->value=*(DLR+offset++);
    		root=temp;
    		if (beg==end)//递归退出条件
    		{
    			root->pleft=NULL;//-----------------------此处是关键,注意啊
    			root->pright=NULL;
    			return;
    		}
    	}
    	else
    	{
                      flag=faulse;
    		return;
    	}
    		searchbuild(DLR,offset,LDR,beg,loc-1,(root->pleft));//递归过程及其回溯过程,loc在此会有反应
    		searchbuild(DLR,offset,LDR,loc+1,end,(root->pright));
    }

            优化后,递归函数重入次数变为15次,重入次数明显减少,效率提高,运行结果为:


    2.3、方案二

            采用分治法,每次递归将子树分长左右两部分,直到节点所在子树长度为0时,返回。

            DLR-s为先序遍历结果,LDR-s为中序遍历结果,lengh。[beg, end]为LDR的有效区间,root为根节点,flag判断是否建树成功。flag的初始值为ture,执行完下面的程序后,若flag的值仍为ture,则说明建树成功,否则建树失败,说明所给的结果格式不合法。

    void searchbuild2(char *DLR_s,char *LDR_s,int lenght,node *&root,bool &flag)
    {
    	int loc,leftlen,rightlen;
    		num++;//统计递归进入次数
    	if (lenght==0)
    	{
    		root=NULL;
    		return;
    	}
    	if (charisexist(*(DLR_s),LDR_s,0,lenght-1,loc))
    	{
    		node *temp=new node;
    		temp->value=*DLR_s;
    		root=temp;
    	}
    	else
    	{
    		flag=faulse;
    		return;
    	}
    	leftlen=loc;
    	rightlen=lenght-loc-1;
    	searchbuild2(DLR_s+1,LDR_s,leftlen,root->pleft);
    	searchbuild2(DLR_s+1+leftlen,LDR_s+leftlen+1,rightlen,root->pright);//注意要越过根节点//LDR_s+leftlen+1
    }

    运行结果:


     

    2.4、方案二的优化

           当节点所在的子树长度length==1时,说明该节点为叶子节点,此处将其左右指针赋NULL后return,否则程序将进行下一层遍历后return,增加了遍历次数,增加了时间。优化后为:

    void searchbuild2(char *DLR_s,char *LDR_s,int lenght,node *&root,bool &flag)
    {
    	int loc,leftlen,rightlen;
    		num++;//统计递归进入次数
    	if (num==0)//格式初步检查,其实可以不要的啦
    	{
    		if (sizeof(DLR_s)!=sizeof(LDR_s)||!DLR_s||!LDR_s)
    		{
    			flag=faulse;		
                       }
    	}
    	if (lenght==0)
    	{
    		root=NULL;
    		return;
    	}
    	if (charisexist(*(DLR_s),LDR_s,0,lenght-1,loc))
    	{
    		node *temp=new node;
    		temp->value=*DLR_s;
    		root=temp;
    		if (lenght==1)
    		{
    			root->pleft=NULL;
    			root->pright=NULL;
    			return;
    		}
    	}
    	else
    	{
    		flag=faulse;		
                       return;
    	}
    	leftlen=loc;
    	rightlen=lenght-loc-1;
    	searchbuild2(DLR_s+1,LDR_s,leftlen,root->pleft);
    	searchbuild2(DLR_s+1+leftlen,LDR_s+leftlen+1,rightlen,root->pright);//注意要越过根节点//LDR_s+leftlen+1
    }

    运行结果:


    2.5、方案三

           方案三是对方案二的改进,将每一节点的左右指针都先赋NULL,若节点所在的子树长度大于1,则在下次递归中更改其指针的值。直到节点所在的子树为1则return,说明此节点为叶子节点。若某一节点的左子树或右子树为空,则递归的if条件将会判断是否进入递归。这样改进后,递归重入次数再次减少,效率提高。

    void searchbuild4(char *DLR_s,char *LDR_s,int lenght,node *&root,bool &flag)//pleft,pright,默认为空lenght==1退出
    {
    	int loc,leftlen,rightlen;
    		num++;
    	if (num==0)//格式初步检查,其实可以不要的啦
    	{
    		if (sizeof(DLR_s)!=sizeof(LDR_s)||!DLR_s||!LDR_s)
    		{
    			flag=false;
    			return ;
    		}
    	}
    	if (charisexist(*(DLR_s),LDR_s,0,lenght-1,loc))
    	{
    		node *temp=new node;
    		temp->value=*DLR_s;
    		temp->pleft=NULL;
    		temp->pright=NULL;
    		root=temp;
    		if (lenght==1)//-----------lengh==1直接返回,因为,默认的每一节点的左右指针都赋NULL,若其为非空,则下次递归时改为子树的指针
    		{
    		    return;
    		}
    	}
    	else
    	{
    		root=NULL;
    		flag=false;
    		return ;
    	}
    	leftlen=loc;
    	rightlen=lenght-loc-1;
    	if (leftlen>0)
    	{
    		searchbuild4(DLR_s+1,LDR_s,leftlen,root->pleft,flag);//此处不可以return;,否则,下面的将不会递归执行
    	}
    	if (rightlen>0)
    	{
    	    searchbuild4(DLR_s+1+leftlen,LDR_s+leftlen+1,rightlen,root->pright,flag);//注意要越过根节点//LDR_s+leftlen+1
    	}
    }

            可以发现,方案三的递归重入次数最少,效率最高。程序运行结果:

    3.后序+中序重建二叉树

           思路:先将后序遍历的LRD的结果反转,反转后的首个字符就是根节点,然后依次是根节点的右子树,左子树。

           反转的代码为:

    void exchangechar(char char1[])
    {
    	char temp;
    	int n=strlen(char1);
    	for (int i=0,j=n-1;i<j;i++,j--)
    	{
    		temp=char1[i];
    		char1[i]=char1[j];
    		char1[j]=temp;
    	}
    }


     

            可采用类似于上述的算法进行重建,现将方案一稍加改进后得代码如下:

    void searchbuild3(char *LRD,int &offset,char *LDR,int beg,int end,node *&rootf,bool &flag)//递归边界条件,递归回溯过程
    {
    	int loc;
    	if (num==0)
    	{
    		if (sizeof(LDR)!=sizeof(LRD)||!LDR||!LRD)
    		{
    			flag=faulse;
    			return;
    		}
    	}
    		num++;
    	if (beg>end)
    	{
    		root=NULL;
    		return;
    	}
    	if(charisexist(*(LRD+offset),LDR,beg,end,loc))//offset无需限定,若offset越界,chariexist返回fause;
    	{
    		node *temp=new node;
    		temp->value=*(LRD+offset++);
    		root=temp;
    		if (beg==end)
    		{
    			root->pleft=NULL;
    			root->pright=NULL;
    			return;
    		}
    	}
    	else
    	{
    		flag=faulse;
    		root=NULL;
    	    return;
    	}
    	searchbuild3(LRD,offset,LDR,loc+1,end,root->pright);//注意此处,先建右子树,再建左子树!!!
    	searchbuild3(LRD,offset,LDR,beg,loc-1,root->pleft);
    }

           运行结果为:

    欢迎各位交流批评指正^_^····

  • 相关阅读:
    python float保留两位小数
    linux使用ftp服务
    selenium是如何启动浏览器的
    小白学PYTHON时最容易犯的6个错误,看看你遇到过几个
    为什么你需要测试某个字段为空的场景
    为什么要做接口测试
    从爱奇艺招聘信息看当前测试开发的技能要求
    测试同学难道要写一辈子的hello world?
    收藏清单: python测试框架最全资源汇总
    如何写出测不出bug的测试用例
  • 原文地址:https://www.cnblogs.com/chhuach2005/p/3627014.html
Copyright © 2011-2022 走看看