码迷,mamicode.com
首页 > 编程语言 > 详细

树与二叉树基础算法的比较

时间:2015-08-19 13:14:48      阅读:133      评论:0      收藏:0      [点我收藏+]

标签:

一:树的创建

在数据结构中,树是以二叉树的形式储存的。

树转换为二叉树形式分为三步:

⑴加线——树中所有相邻兄弟之间加一条连线。

⑵去线——对树中的每个结点,只保留它与第一个孩子结点之间的连线,删去它与其它孩子结点之间的连线。

⑶层次调整——以根结点为轴心,将树顺时针转动一定的角度,使之层次分明。

 

转换后结果如图:

技术分享

 

所以树的创建算法有两个思路:

1.将树转化为二叉树后,以二叉树中结点的关系输入而创建树。

2.直接以树中结点的关系输入,用代码转换为相应的二叉树。

 

 

第一种方法实际就是二叉树创建,只不过是先把树结构转化为二叉树结构,然后再输入创建。

算法思路:

1.每个结点有三个数据信息需要输入,分别是fa,ch,lrflag。(fa是父元素结点的数据,用于找到父元素结点的位置,ch为输入结点的数据,lrflag记录插入父元素的左孩子还是右孩子)

2.每输入一个结点的信息,先进队列,再通过fa在队列中得到父元素结点的地址,最后根据lrflag将新结点插入相应的位置。

3.当输入的ch为指定‘#‘,即无数据时,停止循环。

void CreatBitree2(BiTree *T)  //读边按层次创建二叉树
{
    LinkQueue Q;
    InitQueue(&Q);
    BiTree p,s;
    char fa,ch;
    int lrflag;
    setbuf(stdin,NULL);                                     //清空键盘输入
    scanf("%c%c%d",&fa,&ch,&lrflag);
    while(ch != #)
    {
        p = (BiTree)malloc(sizeof(BiNode)); //二叉树结点p用来记录新数据
        p->data = ch;
        p->lchild = p->rchild = NULL;           //置空
        EnQueue(&Q,p);                                  //父结点入队列
        if(fa == #)
        {
            *T = p;                                                 //如果fa为# 则p为总的根结点T
        }
        else
        {
            GetHead(Q,&s);
            while(s->data != fa)        //找到插入结点的父结点的位置
            {
                DeQueue(&Q,&s);
                GetHead(Q,&s);
            }
            // 此时s为父结点
            if(lrflag == 0)                     //如果子结点标记为0,将新结点p与父节点lchild连接
            {
                s->lchild = p;
            }
            else if(lrflag == 1)
            {
                s->rchild = p;              //同理,p连上父结点rchild
            }
            else
            {
                free(p);
                printf("输入错误!按任意键重新输入....");    //标志输入错误,新结点释放重新输入
                getch();
            }
        }
        setbuf(stdin,NULL);                             //清空键盘输入
        scanf("%c%c%d",&fa,&ch,&lrflag);
    }
}

 

 

第二种方法就是直接输入树中结点,通过代码的处理转化为二叉树结构,与第一种方法的处理顺序是相反的。

这种算法与第一种的二叉树创建的算法有一点区别,因为没有左右孩子,树的结构体定义与二叉树不同,所以不需要lrflag,而是将有相同父元素的结点以兄弟指针连起来。

算法思路:

1.每个结点有二个数据信息需要输入,分别是fa,ch.(fa是父元素结点的数据,用于找到父元素结点的位置,ch为输入结点的数据)

2.每输入一个结点的信息,先进队列,再通过fa在队列中得到父元素结点的地址,如果父元素结点的firstchild指针为空,则firstchild连接上新结点。若不为空,则连上当前父元素的最后一个兄弟结点的nextsibling指针。

3.当输入的ch为指定‘#‘,即无数据时,停止循环。

void CreateCSTree(CSTree *T)//树的按边层次创建
{
    LinkQueue Q;
    InitQueue(&Q);
    CSTree p,s,sign;
    char fa,data;
    printf("请输入新建结点的父元素和子元素的数据:");
    scanf("%c %c",&fa,&data);  //scanf(" %c") 一个空格在加上%c:表示读取遇到的第一个非空格字符
    getchar();
    while(data != #)
    {
        s = (CSTree)malloc(sizeof(CSNode));
        s->data = data;
        s->firstchild = s->nextsibling = NULL;
        EnQueue(&Q,s);
        if(fa == #)
        {
            *T = s;
        }
        else
        {
            GetHead(Q,&p);
            while(p->data != fa)
            {
                DeQueue(&Q,&p);
                GetHead(Q,&p);
            }
            //此时p为父元素结点地址
            if(p->firstchild == NULL)//父元素的firstchild连接
            {
                p->firstchild = s;
                sign = s;            //父元素firstchild已被连接,用sign记住s;
            }
            else                    //父元素其他孩子连在sign的nextsibling上
            {
                sign->nextsibling = s;
                sign = s;            //依然记住,便于下一个结点连接
            }
        }
        printf("请输入新建结点的父元素和子元素的数据:");
        scanf("%c %c",&fa,&data);
        getchar();
    }
}

二:树的遍历

树的遍历和二叉树的遍历差不多,只是根据结构的调整,没有中序遍历。

不过有两个规则:

1.二叉树的前序遍历是树的前序遍历。

2.二叉树的中序遍历是树的后序遍历。

利用这两个规则可以直接使用二叉树的遍历算法来相应的遍历树。

二叉树的遍历算法先前已经写过总结,移步:http://www.cnblogs.com/ecizep/p/4719553.html

此处不再赘述。

三:根结点到叶子结点所有的路径

二叉树:

二叉树求从根结点到叶子结点的路径可以借助前序递归遍历的思想。

算法思路:

1.按照前序递归的思路遍历二叉树且对每个遍历的结点进栈,但是不访问

2.然后当遇到叶子结点时,按从栈底到栈顶的顺序输出栈中的数据(即为一个根结点到叶子结点的路径),然后出栈叶子结点

3.前序递归遍历的过程不断遇到叶子节点并输出路径,直到根结点出栈

 

void DispPath(BiTree T,SqStack *s) //输出所有的根到叶子结点的路径
{
    Bitree p;
    if(T)
    {
        Push(s,p);
        if(T->lchild == NULL && T->rchild == NULL)
        {
            PrintStack(*s);
            printf("\n");
        }
        else
        {
            DispPath(T->lchild,s);
            DispPath(T->rchild,s);
        }
        Pop(s),&p);            
    }
}

 

树:

由于树是以二叉树的形式存储的,所以实际的树的叶子结点与二叉树叶子结点不同。

树的叶子节点:firstchild指针为NULL的结点。

除了这个差别带来的算法带来的if条件语句的差异之外,还有一点与二叉树的根到叶子结点算法不同。

都是使用前序递归遍历的思想,但是树一直向firstchild方向遍历,遇到叶子结点,则向兄弟结点nextsibling遍历。

且需要将firstchild结点先出栈后再遍历兄弟结点,所以需要将上述的算法中向右子树的遍历放在出栈之后。

 

算法思路:

1.从根结点一直向firstchild方向遍历,并进栈。

2.若遍历到叶子结点则输出路径,且出栈叶子结点,然后转向兄弟结点nextsibling。

3.重复1,2步骤

void DispPath(CSTree T,SqStack *s)//输出树从根结点到叶子结点的所有路径
{
    CSTree temp = T;
    if(T)
    {
        Push(s,temp);
        if(T->firstchild == NULL) //第一个孩子结点为空说明当前结点为叶子结点
        {
            PrintStack(*s);
            printf("\n");
        }
        else        //一直向左遍历
        {
            DispPath(T->firstchild,s);
        }
        Pop(s,&temp);    
        if(T->nextsibling)    //必须保证firstchild出栈后,才能向兄弟结点遍历
        {
            DispPath(T->nextsibling,s);
        }
    }
}

四:数据插入与删除

数据的插入,二叉树和树都是差不多的,插入结点是叶子结点,都是先找到父结点,然后插入。

二叉树则是分左右孩子,树则是fistchild存在,就插入兄弟结点。

 

在树中数据的插入

算法思路:

1.根据父元素数据找到父元素的位置

2.若当前父元素firstchild为空,则将数据连到firstchild上,否则则插入到最后一个兄弟结点上。

 

void SearchCST(CSTree T,char keyword,CSTree *p)//查找插入位置
{
    if(T)
    {
        if(T->data == keyword)
        {
            *p = T;
        }
        if(p)
        {
            SearchCST(T->firstchild,keyword,p);
            SearchCST(T->nextsibling,keyword,p);
        }
    }
}


int Insert(CSTree T,char fa,char data)//数据插入
{
    CSTree pos = NULL,s;                //pos记录新结点插入地址
    if(T == NULL)
    {
        return 0;
    }
    SearchCST(T,fa,&pos);
    if(pos)        //在树中找到插入结点地址
    {
        s = (CSTree)malloc(sizeof(CSNode));
        s->firstchild = s->nextsibling = NULL;
        s->data = data;
        if(pos->firstchild == NULL)//根的第一个孩子不存在则连上firstchild指针
        {
            pos->firstchild = s;
        }
        else                        //根的第一个孩子存在,连上nextsibling末尾
        {
            pos = pos->firstchild;
            while(pos->nextsibling)
            {
                pos = pos->nextsibling;
            }
            pos->nextsibling = s;
        }
        return 1;
    }
    else
    {
        printf("父结点数据输入错误,在树中找不到此结点数据\n");
        return 0;
    }
}

 

二叉树结构数据的删除分2两种:

1.叶子结点直接删除

2.非叶子结点则删除此结点下的所有子结点

 

树结构中数据的删除分以下几个情况:

1.当删除结点为根结点时,直接摧毁整棵树

2.当删除结点为firstchild结点时,删除包括firstchild在内的下面的所有子结点

3.当删除结点是nextsibling结点时,删除包括nextsibling在内的下面的所有子结点,同时如果nextsibling不为NULL,则需连接

 

算法思路和插入差不多,先找到位置,然后根据不同的情况删除当前结点。

 

int DeletePoint(CSTree *T,char fa,char data) //删除一个结点  因为删除结点可能是树根,所以*T
{
    CSTree p = NULL,s;
    SearchCST(*T,fa,&p); //找父结点位置
    if(fa == #)        //删除结点是树根的情况
    {
        CleanCSTree(*T);
        *T = NULL;
        return 1;
    }
    if(p == NULL)        
    {
        return 0;
    }
    else  //父结点找到,继续对子结点情况分类
    {
        if(p->firstchild->data == data)    //删除结点是firstchild的情况
        {
            s = p->firstchild;            //记住删除结点地址
            p->firstchild = s->nextsibling;        //连接上后面的结点
            s->nextsibling = NULL;
            CleanCSTree(s);                //清空以删除结点为树根的树
        }
        else                            //删除结点是兄弟结点时
        {
            p = p->firstchild;            //p指向第一个孩子结点
            while(p->nextsibling && p->nextsibling->data != data) //用循环找到要删除的结点
            {
                p = p->nextsibling;
            }
            if(p->nextsibling->data == data)    //此时p状态:p指向删除结点的前一个结点
            {
                s = p->nextsibling;
                p->nextsibling = s->nextsibling; //将p的nextsibling连接到删除结点的后面一个结点
                s->nextsibling = NULL;
                CleanCSTree(s);            //清空以删除结点为树根的树
                return 1;
            }
            else        //没有找到结点,删除失败
            {    
                return 0;
            }
        }
    }    
    return 1;
}

 

树与二叉树基础算法的比较

标签:

原文地址:http://www.cnblogs.com/ecizep/p/4738104.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!