05数据结构——树与二叉树

5.2.2二叉树的存储结构

1.顺序存储结构

2.链式存储结构 

二叉树的链式存储结构描述:

typedef struct BiTNode{
    ElemType data;                     //数据域
    struct BiTNode *lchild,*rchild;    //左、右孩子指针
}BiTNode,*BiTree;

5.3.1二叉树的遍历 

1.先序遍历(根左右)

void PreOrder(BiTree T){
    if(T!=NULL){
        visit(T);               //访问根结点
        PreOrder(T->lchild);    //递归遍历左子树
        PreOrder(R->rchild);    //递归遍历右子树
    }
}

2.中序遍历(左根右)

void InOrder(BiTree T){
    if(T!=NULL){
        InOrder(T->lchild);    //递归遍历左子树
        visit(T);              //访问根结点
        InOrder(T->rchild);    //递归遍历右子树
    }
}

3.后序遍历(左右根)

void PostOrder(BiTree T){
    if(T!=NULL){
        PostOrder(T->lchild);    //递归遍历左子树
        PostOrder(T->rchild);    //递归遍历右子树
        visit(T);                //访问根结点
    }
}

4.递归算法和非递归算法的转换 

(1).中序遍历的非递归算法如下:

void InOrder2(BiTree T){
    InitStack(S); BiTree p=T;        //初始化栈S;p是遍历指针
    while(p||!IsEmpty(S)){           //栈不空或p不空时循环
        if(p){                       //一路向左
            Push(S,p);               //当前结点入栈
            p=p->lchild;             //左孩子不空,一直向左走
        }
        else{                        //出栈,并转向出栈结点的右子树
            Pop(S,p); visit(p);      //栈顶元素出栈,访问出栈结点
            p=->rchild;              //向右子树走,p赋值为当前结点的右孩子
        }                            //返回while循环继续进入if-else语句
    }
}

(2).先序遍历的非递归算法如下:

void PreOrder2(BiTree T){
    InitStack(S); BiTree p=T;        //初始化栈S;p是遍历指针
    while(p||!IsEmpty(S)){           //栈不空或p不空时循环
        if(p){                       //一路向左
            visit(p);Push(S,p);      //访问当前结点,并入栈
            p=p->lchild;             //左孩子不空,一直向左走
        }
        else{                        //出栈,并转向出栈结点的右子树
            Pop(S,p);                //栈顶元素出栈
            p=p->rchild;             //向右子树走,p赋值为当前结点的右孩子
        }                            //返回while循环继续进入if-else语句
    }
}

(3).后序遍历的非递归算法如下:

void PostOrder3(BiTree T){
    InitStack(S);
    BiTNode *p=T;
    BiTNode *r=NULL;
    while(p||!IsEmpty(S)){
        if(p){                          //走到最左边
            push(S,p);
            p=p->lchild;
        }
        else{                           //向右
            GetTop(S,p);                //读栈顶结点(非出栈)
            if(p->rchild&&p->rchild!=r) //若右子树存在,且未被访问过
                p=p->rchild;            //转向右
            else{                       //否则,弹出结点并访问
                pop(S,p);               //将结点弹出
                visit(p->data);         //访问该结点
                r=p;                    //记录最近访问过的结点
                p=NULL;                 //结点访问完后,重置p指针
            }
        }//else
    }//while
}

5.层次遍历

二叉树的层次遍历算法如下:

void LevelOrder(BiTree T){
    InitQueue(Q);                //初始化辅助队列
    BiTree p;                    
    EnQueue(Q,T);                //将根结点入队
    while(!IsEmpty(Q)){          //队列不空则循环
        DeQueue(Q,p);            //队头结点出队
        visit(p);                //访问出队结点
        if(p->lchild!=NULL)      
            EnQueue(Q,p->lchild);    //左子树不空,则左子树根结点入队
        if(p->rchild!=NULL)
            EnQueue(Q,p->rchild);    //右子树不空,则右子树根结点入队
    }
}

5.3.2线索二叉树

1.线索二叉树的基本概念

线索二叉树的存储结构描述:

typedef struct ThreadNode{
    ElemType data;                        //数据元素
    struct ThreadNode *lchild,*rchild;    //左、右孩子指针
    int ltag,rtag;                        //左、右线索标志
}ThreadNode,*ThreadTree;

 2.中序线索二叉树的构造

通过中序遍历对二叉树线索化的递归算法如下:

void InThread(ThreadTree &p,ThreadTree &pre){
    if(p!=NULL){
        InThread(p->lchild,pre);    //递归,线索化左子树
        if(p->lchild==NULL){        //左子树为空,建立前驱线索
            p->lchild=pre;
            p->ltag=1;
        }
        if(pre!=NULL&&pre->rchild==NULL){
            pre->rchild=p;          //建立前驱结点的后继线索
            pre->rtag=1;
        }
        pre=p;                      //标记当前结点成为刚刚访问过的结点
        InThread(p->rchild,pre);    //递归,线索化右子树
    }//if(p!=NULL)
}    

通过中序遍历建立中序线索二叉树的主要过程算法如下:

void CreateInThread(ThreadTree T){
    ThreadTree pre=NULL;
    if(T!=NULL){                //非空二叉树,线索化
        InThread(T,pre);        //线索化二叉树
        pre->rchild=NULL;       //处理遍历的最后一个结点
        pre->rtag=1;            
    }
}

3.先序线索二叉树的构造 

通过先序遍历对二叉树线索化的递归算法如下:

void PreThread(ThreadTree &p,ThreadTree &pre){
    if(p!=NULL){
        if(p->lchild==NULL){            //左子树为空,建立前驱线索
            p->lchild=pre;
            p->ltag=1;
        }
        if(pre!=NULL&&pre->rchild==NULL){
            pre->rchild=p;             //建立前驱结点的后继线索
            pre->rtag=1;
        }
        pre=p;
        if(p->ltag==0){                //lchild不是前驱线索
            PreThread(p->lchild,pre);  //递归,线索化左子树
        }
        PreThread(p->rchild,pre);     //递归,线索化右子树
    }
}   

通过先序遍历建立中序线索二叉树的主要过程算法如下:

void CreatePreThread(ThreadTree T){
    ThreadTree pre=NULL;
    if(T!=NULL){                //非空二叉树,线索化
        PreThread(T,pre);       //线索化二叉树
        pre->rchild=NULL;       //处理遍历的最后一个结点
        pre->rtag=1;
    }
}

 4.后序线索二叉树的构造

 通过后序遍历对二叉树线索化的递归算法如下:

void PostThread(ThreadTree &p,ThreadTree &pre){
    if(p!=NULL){
        PostThread(p->lchild,pre);        //递归,线索化左子树
        PostThread(p->rchild,pre);        //递归,线索化右子树
        if(p->lchild==NULL){              //左子树为空,建立前驱线索
            p->lchild=pre;
            pre->ltag=1;
        }
        if(pre!=NULL&&pre->rchild==NULL){
            pre->rchild=p;                //建立前驱结点的后继线索
            pre->rtag=p;
        }
        pre=p;
    }
}        

通过后序遍历建立中序线索二叉树的主要过程算法如下:

void CreatePostThread(ThreadTree T){
    ThreadTree pre=NULL;
    if(T!=NULL){
        PostThread(T,pre);
        pre->rchild=NULL;
        pre->rtag=1;
    }
}

 6.中序线索二叉树顺向中序遍历

1)求中序线索二叉树中中序序列下的第一个结点:

ThreadNode *Firstnode(ThreadNode *p){ 
    while(p->ltag==0) p=p->lchild;    //最左下结点(不一定是叶结点)
    return p;
}

2)求中序线索二叉树中结点p在中序序列下的后继:

ThreadNode *Nextnode(ThreadNode *p){
    if(p->rtag==0) 
        return Firstnode(p->rchild);
    else
        return p->rchild;    //rtag==1 直接返回后继线索
}

3)利用上面两个算法,可以写出不含头结点的中序线索二叉树的中序遍历算法:

void Inorder(ThreadNode *T){
    for(ThreadNode *p=Firstnode(T);p!=NULL;p=Nextnode(p))
        visit(p);
}

7.中序线索二叉树逆向中序遍历

1)求中序线索二叉树中中序序列下的最后一个结点:

ThreadNode *Lastnode(ThreadNode *p){
    while(p->rtag==0) p=p->rchild;    //最右下结点(不一定是叶结点)
    return p;
}

2)求中序线索二叉树中结点p在中序序列下的前驱:

ThreadNode *Prenode(ThreadNode *p){
    if(p->ltag==0)  
        return Lastnode(p->lchild);
    else
        return p->lchild;        //ltag==1直接访问前驱线索
}

 3)利用上面两个算法,可以写出不含头结点的中序线索二叉树的逆向中序遍历算法:

void RevInorder(ThreadNode *T){
    for(ThreadNode *p=Lastnode(T);p!=NULL;p=Prenode(p))
        visit(p);
}

5.4.1树的存储结构

1.双亲表示法

双亲表示法的存储结构描述如下:

#define MAX_TREE_SIZE 100            //树中最多结点数
typedef struct{                      //树的结点定义
    ElemType data;                   //数据元素
    int parent;                      //双亲位置域
}PTNode;
typedef struct{                      //树的类型定义
    PTNode nodes[MAX_TREE_SIZE];     //双亲表示
    int n;                           //结点数
}PTree;

2.孩子表示法

3.孩子兄弟表示法 

孩子兄弟表示法的存储结构描述如下:

typedef struct CSNode{
    ElemType data;                             //数据域
    struct CSNode *firstchild,*nextsibling;    //第一个孩子和右兄弟指针
}CSNode,*CSTree;

 5.5.1哈夫曼树和哈夫曼编码

 2.哈夫曼树的构造

5.5.2并查集 

1)并查集的结构定义如下:

#define SIZE 100
int UFSets[SIZE];    //集合元素数组(双亲指针数组)

2)并查集的初始化操作(S即为并查集):

void Initial(int S[]){
    for(int i=0;i<SIZE;i++)        //每个自成单元元素集合
        S[i]=1;
}

3)Find操作(函数在并查集S中查找并返回包含元素x的树的根):

int Find(int S[],int x){
    while(S[x]>=0)            //循环寻找x的根
        x=S[i];
    return x;                //根的S[]小于0
}

 4)Union操作(函数求两个不相交子集合的并集):

void Union(int S[],int Root1,int Root2){
    if(Root1==Root2) return;        //要求Root1和Root2是不同的集合
    S[Root2]=Root1;                 //将根Root2连接到另一根Root1下面
}

posted @ 2023-10-10 17:58  freshman_xy  阅读(35)  评论(0)    收藏  举报  来源