浅析数据结构之树与森林

来源:互联网 发布:java面试 骆昊csdn 编辑:程序博客网 时间:2024/04/29 07:20

树是一种重要的非线性数据结构(二叉树是每个结点最多有两个子树的有序树)

  1. 二叉树的定义:有且仅有一个根结点,除根结点外,每个结点只有一个父结点,最多含有两个子结点,子点有左右之分。
  2. 存储结构
    • 二叉树的存储结构可以采用顺序存储,也可以采用链式存储,其中链式存储更加灵活。
    •  在链式存储结构中,与线性链表类似,二叉树的每个结点采用结构体表示,结构体包含三个域:数据域、左指针、右指针。

二叉树的创建和遍历

#include<stdio.h>//typedef int Elemment;typedef char Elemment;typedef struct node{    Elemment data;    struct node *lchild,*rchild;        //左右子结点指针} BinTree;/*测试数据*///HDB#A##C##G#FE###//1 2 3 -1 4 -1 -1 5 -1 -1 6 -1 7 8 -1 -1 -1//先序创建二叉树BinTree *CreateBinTree(Elemment *e,int *i){    BinTree *bt = NULL;    //if(e[(*i)++]!=-1)    if(e[(*i)++]!='#')    {        bt = (BinTree*)malloc(sizeof(BinTree));        bt->data = e[(*i)-1];        bt->lchild=CreateBinTree(e,i);        bt->rchild=CreateBinTree(e,i);    }    return bt;}//先序遍历二叉树void firstorder(BinTree *bt){    if(bt!=NULL)    {        printf("%c",bt->data);        //printf("%d",bt->data);        Inorder(bt->lchild);        Inorder(bt->rchild);    }}//中序遍历二叉树void Inorder(BinTree *bt){    if(bt!=NULL)    {        Inorder(bt->lchild);           printf("%c",bt->data);        //printf("%d",bt->data);        Inorder(bt->rchild);    }}//后序遍历二叉树void lastorder(BinTree *bt){    if(bt!=NULL)    {        Inorder(bt->lchild);        Inorder(bt->rchild);        printf("%c",bt->data);        //printf("%d ",bt->data);    }}int main(){    int i=0,j=0;    //int c[100];    //while(~scanf("%d",&c[j++]));    char c[100];    gets(c);    BinTree *bt = CreateBinTree(c,&i);    Inorder(bt);}
-------------------------------------------------------------------------------------------分割线-------------------------------------------------------------------------------------
      二叉树线索化是一种可以简化二叉树遍历的算法(当以二叉链表作为二叉树的存储结构时,只能找到结点的左右子节点,而不能直接找到前趋和后继,只有在遍历的动态       过程中才能找到这些信息,如果在第一次遍历时就将这些信息保存起来,那么第二次遍历时就可将二叉树作为线性结构进行访问,从而简化遍历操作)
       n个结点的二叉链表中含有n+1(2n-(n-1)=n+1)个空指针域。利用二叉链表中的空指针域,存放指向结点在某种遍历次序下的前趋和后继结点的指针 (这种附加的指针称为"线索")。
      加上线索的二叉树称为线索二叉树,对二叉树以某种遍历方式使其加上线索的过程称为线索化。

--------------------------------------------------------------------------------------------分割线------------------------------------------------------------------------------------

二叉树的线索化规定如下:

       若结点右左子树,则其lchild域指向其左孩子,否则令lchild域指向其前驱;若结点有右子树,其rchild域指向其右孩子,否则指向其后继。
         线索二叉树的描述如下

typedef struct node{    int ltag,rtag;                /*左右标志域,ltag=0,lchild为左指针,ltag=1,lchild为左线索;                                                rtag=0,rchild为右指针,rtag=1,rchild为右线索;*/    Elemment data;    struct node *lchild,*rchild;    //左右子节点指针} BinTree;

为了操作方便,给线索链表添加一个”头结点“,该结点的指针lchild指向二叉树的根结点,ltag=0;rchild指向二叉树遍历序列的最后一个结点,rtag=1;同时让遍历序列中第一个结点的左线索和最后一个结点的右线索指向头结点,这样线索链表就构成了一个双向循环链表(可逆序可正序遍历)

下面以中序线索化为例,创建一个中序线索链表

BinTree *pre;   //定义一个全局变量。始终指向当前结点的前趋//中序线索化BinTree *InorderThreading(BinTree *bt){    BinTree *head;        //添加”头结点“    head = (BinTree*)malloc(sizeof(BinTree));    if(!head)    {        printf("分配失败");        return NULL;    }    head->ltag = 0;    head->rtag = 1;    head->rchild = head;    if(bt==NULL)    {        head->lchild = head;   //若二叉树为空,head左线索指向自身    }    else    {        head->lchild = bt;     //若二叉树非空,head左线索指向根结点        pre = head;        InThreading(bt);        //中序遍历进行线索化        pre->rchild = head;     //最后一个结点右线索化        pre->rtag = 1;        head -> rchild = pre;    //头结点的右指针指向最后一个结点    }    return head;}void InThreading(BinTree *bt){    if(bt!=NULL)    {        InThreading(bt->lchild);        if(bt->lchild==NULL)        {            bt->ltag=1;            bt->lchild = pre;        }        if(pre->rchild==NULL)        {            pre->rtag=1;            pre->rchild = bt;        }        pre = bt;        InThreading(bt->rchild);    }}

中序线索二叉树的遍历

  • 若该结点的右标志rtag==1,表明该结点没有右孩子,则rchild为线索,直接指示后继。
  • (逆序遍历时,若该结点的左标志ltag==1,表明该结点没有左孩子,则lchild为线索,直接指示前趋。)
  • 若该结点的右标志rtag==0,表明该结点有右孩子,结点的后继应是遍历其右子树访问的第一个结点,即最左下结点。
  • (逆序遍历时,若该结点的左标志ltag==0,表明该结点有左孩子,结点的前趋应是其左孩子)

//中序线索二叉树的遍历void Inorder_T(BinTree *head){    BinTree *bt;    bt = head->lchild;    while(bt!=head)    {        while(!bt->ltag)        bt=bt->lchild;        //printf("%d",bt->data);        printf("%c",bt->data);        while(bt->rtag==1&&bt->rchild!=head)        {            bt=bt->rchild;            //printf("%d",bt->data);            printf("%c",bt->data);        }        bt=bt->rchild;    }}

树的存储结构有几种

  • 双亲表示法(习惯用顺序存储)
  • 孩子链表表示法(其指针域指向由其所有孩子结点组成的单链表首结点)
  • 孩子兄弟表示法,一个指针域指向其第一个孩子,另一个指针域指向其所有兄弟组成的单链表首结点

树与二叉树的转换

从树的二叉链表定义(如上红色两种表示,相当于二叉树,解释不同而已)可知,任何一棵树所对应的二叉链表其右子树必为空
(树与二叉树是一一对应的且唯一)

森林与二叉树的转换

左子树为第一棵树转换,右子树由剩下的森林转换(反之亦然)

0 0
原创粉丝点击