孩子兄弟表示法(二叉链表树)

考虑下面这森林:


如果用孩子兄弟表示法可以表示为:


顾名思义,孩子兄弟表示法的每个节点有两个指针域,一个指向其长子,另一个指向其兄弟.

实现:
/**************************************************
树的孩子兄弟表示法(二叉链表树)
by Rowandjj
2014/5/25
**************************************************/
#include<iostream>
using namespace std;

typedef char ElemType; 
//--------二叉链表(孩子-兄弟)存储表示-------
typedef struct _TREENODE_
{
    struct _TREENODE_ *pFirstChild;
    struct _TREENODE_ *pNextSibling;
    ElemType data;
}TreeNode,*pTreeNode,**ppTreeNode;

//----------辅助队列-------------------
typedef struct _QUEUENODE_
{
    struct _QUEUENODE_ *pNext;
    pTreeNode data;
}QueueNode,*pQueueNode;

typedef struct _QUEUE_
{
    pQueueNode pHead;
    pQueueNode pTail;
    int nodeCount;
}Queue,*pQueue;

//-----------队列操作定义-----------------------
void InitQueue(pQueue pQueueTemp);
void Enqueue(pQueue pQueueTemp,pTreeNode pTreeNodeTemp);
void Dequeue(pQueue pQueueTemp,ppTreeNode ppTreeNodeTemp);
bool IsQueueEmpty(Queue QueueTemp);
void DestroyQueue(pQueue pQueueTemp);


//------------二叉树操作定义--------------------

void CreateTree(ppTreeNode ppTreeNodeTemp);
void DestroyTree(ppTreeNode ppTreeNodeTemp);
int GetDepth(pTreeNode pTreeNodeTemp);

void PreTravel(pTreeNode pTreeNodeTemp);
void PostTravel(pTreeNode pTreeNodeTemp);
void MidTravel(pTreeNode pTreeNodeTemp);
void LevelTravel(pTreeNode pTreeNodeTemp);

pTreeNode Point(pTreeNode pTreeNodeTemp,ElemType e);
ElemType GetParent(pTreeNode pTreeNodeTemp,ElemType e);

void InsertTree(ppTreeNode ppTreeNodeTemp,ElemType data,int i,pTreeNode pSub);//将psub插入到ppTreeNodeTemp中的值为data的节点的第i个子树
void DeleteTree(ppTreeNode ppTreeNodeTemp,ElemType data,int i);//删除ppTreeNodeTemp中的值为data的节点的第i个子树


//---------队列操作实现---------------
void InitQueue(pQueue pQueueTemp)
{
    pQueueTemp->pHead = pQueueTemp->pTail = (pQueueNode)malloc(sizeof(QueueNode));
    if(pQueueTemp->pHead == NULL)
    {
        return;
    }
    pQueueTemp->nodeCount = 0;
    pQueueTemp->pHead->pNext = NULL;
}
void Enqueue(pQueue pQueueTemp,pTreeNode pTreeNodeTemp)
{
    if(pQueueTemp == NULL)
    {
        return;
    }
    pQueueNode pNew = (pQueueNode)malloc(sizeof(QueueNode));
    if(pNew == NULL)
    {
        return;
    }
    pNew->data = pTreeNodeTemp;
    pNew->pNext = NULL;

    pQueueTemp->pTail->pNext = pNew;
    pQueueTemp->pTail = pNew;
    pQueueTemp->nodeCount++;
}
void Dequeue(pQueue pQueueTemp,ppTreeNode ppTreeNodeTemp)
{
    if(pQueueTemp == NULL)
    {
        return;
    }
    pQueueNode pDel = pQueueTemp->pHead->pNext;
    pQueueTemp->pHead->pNext = pDel->pNext;
    if(pDel == pQueueTemp->pTail)
    {
        pQueueTemp->pTail = pQueueTemp->pHead;
    }
    *ppTreeNodeTemp = pDel->data;
    free(pDel);
    pQueueTemp->nodeCount--;
}
bool IsQueueEmpty(Queue QueueTemp)
{
    return QueueTemp.nodeCount == 0;
}
void DestroyQueue(pQueue pQueueTemp)
{
    if(pQueueTemp != NULL)
    {
        pQueueNode pTravel = pQueueTemp->pHead->pNext;
        while(pTravel != NULL)
        {
            pQueueTemp->pHead->pNext = pTravel->pNext;
            free(pTravel);
            pTravel = pQueueTemp->pHead->pNext;
        }
        free(pQueueTemp->pHead);
        pQueueTemp->nodeCount = 0;
    }
}

//------------二叉树操作实现-------------------------

void CreateTree(ppTreeNode ppTreeNodeTemp)//创建
{
    char szBuffer[20];
    char a;
    cout<<"输入根节点:";
    cin>>a;
    Queue queue;
    InitQueue(&queue);
    if(a != '#')
    {
        *ppTreeNodeTemp = (pTreeNode)malloc(sizeof(TreeNode));
        (*ppTreeNodeTemp)->data = a;
        (*ppTreeNodeTemp)->pNextSibling = NULL;
        Enqueue(&queue,*ppTreeNodeTemp);//入队根节点

        pTreeNode pTemp,pTemp1;
        while(!IsQueueEmpty(queue))
        {
            Dequeue(&queue,&pTemp);
            cout<<"输入"<<pTemp->data<<"的孩子节点:";
            cin>>szBuffer;
            if(szBuffer[0] != '#')
            {
                pTemp->pFirstChild = (pTreeNode)malloc(sizeof(TreeNode));
                
                pTemp->pFirstChild->data = szBuffer[0];
    
                pTemp1 = pTemp->pFirstChild;
                for(int i = 1; i < strlen(szBuffer); i++)
                {
                    pTemp1->pNextSibling = (pTreeNode)malloc(sizeof(TreeNode));
                    Enqueue(&queue,pTemp1);
                    pTemp1->pNextSibling->data = szBuffer[i];
                        
                    pTemp1 = pTemp1->pNextSibling;
                }
                pTemp1->pNextSibling = NULL;
                Enqueue(&queue,pTemp1);

            }else
            {
                pTemp->pFirstChild = NULL;
            }
            
        }

    }else
    {
        *ppTreeNodeTemp = NULL;
    }

}
void DestroyTree(ppTreeNode ppTreeNodeTemp)
{
    if(*ppTreeNodeTemp != NULL)
    {
        if((*ppTreeNodeTemp)->pFirstChild != NULL)
        {
            DestroyTree(&(*ppTreeNodeTemp)->pFirstChild);    
        }
        if((*ppTreeNodeTemp)->pNextSibling != NULL)
        {
            DestroyTree(&(*ppTreeNodeTemp)->pNextSibling);
        }
        free(*ppTreeNodeTemp);
        *ppTreeNodeTemp = NULL;
    }
}


int GetDepth(pTreeNode pTreeNodeTemp)
{
    if(pTreeNodeTemp == NULL)
    {
        return 0;
    }
    if(pTreeNodeTemp->pFirstChild == NULL)
    {
        return 1;
    }
    int depth,max = 0;
    pTreeNode pTemp = pTreeNodeTemp->pFirstChild;
    for(;pTemp != NULL; pTemp = pTemp->pNextSibling)
    {
        depth = GetDepth(pTemp);
        if(depth > max)
        {
            max = depth;
        }
    }
    return max+1;
}

void PreTravel(pTreeNode pTreeNodeTemp)
{
    if(pTreeNodeTemp)
    {
        cout<<pTreeNodeTemp->data<<" ";
        PreTravel(pTreeNodeTemp->pFirstChild);
        PreTravel(pTreeNodeTemp->pNextSibling);
    }
}

void MidTravel(pTreeNode pTreeNodeTemp)
{
    if(pTreeNodeTemp)
    {
        MidTravel(pTreeNodeTemp->pFirstChild);
        cout<<pTreeNodeTemp->data<<" ";
        MidTravel(pTreeNodeTemp->pNextSibling);
    }
}


void PostTravel(pTreeNode pTreeNodeTemp)
{
    if(pTreeNodeTemp)
    {
        PostTravel(pTreeNodeTemp->pFirstChild);
        PostTravel(pTreeNodeTemp->pNextSibling);
        cout<<pTreeNodeTemp->data<<" ";
    }
}

pTreeNode Point(pTreeNode pTreeNodeTemp,ElemType e)
{
    Queue queue;
    InitQueue(&queue);
    if(pTreeNodeTemp != NULL)
    {
        Enqueue(&queue,pTreeNodeTemp);

        pTreeNode pTemp;
        while(!IsQueueEmpty(queue))
        {
            Dequeue(&queue,&pTemp);
            if(pTemp->data == e)
            {
                DestroyQueue(&queue);
                return pTemp;
            }else
            {
                pTreeNode pSibling = NULL;
                if(pTemp->pFirstChild != NULL)
                {
                    Enqueue(&queue,pTemp->pFirstChild);//入队长子
                     pSibling = pTemp->pFirstChild->pNextSibling;
                }
                 
                while(pSibling != NULL)
                {
                    Enqueue(&queue,pSibling);//入队兄弟
                    pSibling = pSibling->pNextSibling;
                }
            }
        }

    }
    return NULL;
}

ElemType GetParent(pTreeNode pTreeNodeTemp,ElemType e)
{
    Queue queue;
    InitQueue(&queue);
    
    if(pTreeNodeTemp != NULL)
    {
        if(pTreeNodeTemp->data == e)
        {
            return -1;
        }
        Enqueue(&queue,pTreeNodeTemp);

        pTreeNode pTemp,pTemp1;
        while(!IsQueueEmpty(queue))
        {
            Dequeue(&queue,&pTemp);
            if(pTemp->pFirstChild)
            {
                if(pTemp->pFirstChild->data == e)
                {
                    return pTemp->data;
                }
                pTemp1 = pTemp;
                
                Enqueue(&queue,pTemp->pFirstChild);//入队长子
                pTemp = pTemp->pFirstChild;

                while(pTemp->pNextSibling)
                {
                    pTemp = pTemp->pNextSibling;
                    if(pTemp->data == e)
                    {
                        return pTemp1->data;
                    }
                    Enqueue(&queue,pTemp);//入队兄弟
                }
            }
        }
    }

    return -1;
}

void LevelTravel(pTreeNode pTreeNodeTemp)//层次遍历
{
    Queue queue;
    InitQueue(&queue);

    if(pTreeNodeTemp != NULL)
    {
        cout<<pTreeNodeTemp->data<<" ";
        Enqueue(&queue,pTreeNodeTemp);
        
        pTreeNode pTemp;
        while(!IsQueueEmpty(queue))
        {
            Dequeue(&queue,&pTemp);
            
            if(pTemp->pFirstChild != NULL)
            {
                pTemp = pTemp->pFirstChild;
                cout<<pTemp->data<<" ";
                Enqueue(&queue,pTemp);//入队长子
                while(pTemp->pNextSibling != NULL)
                {
                    pTemp = pTemp->pNextSibling;
                    cout<<pTemp->data<<" ";
                    Enqueue(&queue,pTemp);
                }
            }

        }    
    }
}

void InsertTree(ppTreeNode ppTreeNodeTemp,ElemType data,int i,pTreeNode pSub)
{
    if(*ppTreeNodeTemp == NULL)
    {
        return;
    }

    pTreeNode pTreeNodeTemp = Point(*ppTreeNodeTemp,data);
    if(pTreeNodeTemp != NULL)
    {
        if(i==1)
        {
            pSub->pNextSibling = pTreeNodeTemp->pFirstChild;
            pTreeNodeTemp->pFirstChild = pSub;
        }else
        {
            int j = 2;
            pTreeNodeTemp = pTreeNodeTemp->pFirstChild;
            while (j < i && pTreeNodeTemp)
            {
                pTreeNodeTemp = pTreeNodeTemp->pNextSibling;
                j++;
            }
            if(j == i)
            {
                pSub->pNextSibling = pTreeNodeTemp->pNextSibling;
                pTreeNodeTemp->pNextSibling = pSub;
            }else
            {
                return;
            }
        }
    }
}

void DeleteTree(ppTreeNode ppTreeNodeTemp,ElemType data,int i)
{
    if(*ppTreeNodeTemp == NULL)
    {
        return;
    }
    pTreeNode pTemp;
    pTreeNode pTreeNodeTemp = Point(*ppTreeNodeTemp,data);
    if(pTreeNodeTemp != NULL)
    {    
        if(i == 1)//删除长子
        {
            pTemp = pTreeNodeTemp->pFirstChild;
            pTreeNodeTemp->pFirstChild = pTemp->pNextSibling;
            pTemp->pNextSibling = NULL;
            DestroyTree(&pTemp);
        }else
        {
            pTreeNodeTemp = pTreeNodeTemp->pFirstChild;
            int j = 2;
            while(j < i && pTreeNodeTemp)
            {
                pTreeNodeTemp = pTreeNodeTemp->pNextSibling;    
                j++;
            }

            if(j == i)
            {
                pTemp = pTreeNodeTemp->pNextSibling;
                pTreeNodeTemp->pNextSibling = pTemp->pNextSibling;
                pTemp->pNextSibling = NULL;
                DestroyTree(&pTemp);
            }
        }
    }
}




  • 38
    点赞
  • 118
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
5.1 数的逻辑结构 5.1.1 1、的定义 在中常常将数据元素称为结点 (1)有且仅有一个特定的称为根的结点; (2)当n>1时,除根结点之外的其余结点被分成m(m>0)个互不相交的有限集合T1,T2,•••Tm,其中每个集合又是一棵,并称为这个节点的子。 2、的基本术语: 结点的度、的度 叶子节点、分支结点 孩子节点、分支结点、兄弟节点 路径、路径长度 祖先、子孙 结点的层数、的深度(高度) 层序编号 有序、无序 森林 5.1.2 的抽象数据类型定义 5.1.3的遍历操作 1、前序遍历 的前序遍历操作定义为: 若为空,则空操作返回;否则 (1)访问根结点 (2)按照从左向右的顺序前序遍历根结点的每一棵子。 2、中序遍历 的中序遍历操作定义为: 若为空,则空操作返回;否则 (1)按照从左向右的顺序后序遍历根结点的每一棵子; (2)访问根结点。 3、层序遍历 的层序遍历也称作的广泛遍历,其操作定义为的第一层开始,自上而下逐层遍历,在同一层中,按从左向右的顺序对结点逐个访问。 5.2的存储结构 5.2.1 双亲表示法的定义可知,中每个结点都有且仅有一个双亲结点。所以利用这一特性,可以用一维数组来存储各个结点,数组中一个元素对应一个结点,数组元素包括中结点的数据信息以及该结点的双亲在数组中的下标。 其中: Data为数据域,存储中结点的数据信息; Parent为指针即游标,存储该结点的双亲在数组中的小标。 5.2.2孩子表示法 1、多重链表表示法 (1)指针域的个数等于该结点的度。 (2)指针域的个数等于的度。 2、孩子链表表示法孩子看成一个线性表,且以单链表存储,称为该结点的孩子链表。则n个结点有n个孩子链表孩子节点有两类:孩子节点、表头结点。 5.2.3 双亲孩子表示法 即将双亲表示法孩子链表表示法相结合的存储方法。仍将各结点的孩子分别组成单链表,同时用一维数组顺序存储中的各结点,数组元素除了包括结点的数据信息和该结点的孩子链表的头指针之外,还增设一个域存储该结点的双亲在数组的下标。 5.2.4孩子兄弟表示法 又称二链表表示法,其方法是链表中每个结点除数据域外,还设置了两个指针分别指向该结点的第一个孩子和右兄弟链表的结构: Firstchild data rightsib 指针域,存储第一个孩子结点的存储地址 数据域,存储该结点的数据信息 指针域,存储该结点右兄弟结点的存储地址 5.3二叉的逻辑结构 最简单的结构,特别适合计算机处理,而且任何数都可以简单的转换为二叉。(重点内容) 5.3.1二叉的定义 二叉是n(n>=0)个结点的有限集合,该集合或者为空集,或者有一个根节点和两棵互不相交的、分别称为根节点的左子和右子二叉组成。 二叉具有五种基本形态: 1、空二叉; 2、只有一个根结点; 3、根结点只有左子; 4、根结点只有右子; 5、根结点既有左子又有右子 特殊二叉: 1、斜; 2、满二叉; 3、完全二叉; 5.3.2二叉的基本性质 性质5-1 二叉的第i层上最多有2^(i-1)个结点(i>=1)。 性质5-2 在一棵深度为k的二叉中,最多有2^k-1个结点,最少有k个结点。 性质5-3 在一棵二叉中,如果叶子结点的个数为n0,度为2的结点个数为n2,则n0=n2+1. 性质5-4 具有n个结点的完全二叉的深度为【log2^n】+1。 性质5-5 对一棵具有n个结点的完全二叉中的结点从一开始按层序编号,则对于任意的编号为i(1<=i<=n)的结点,有: (1)如果i>1,则结点i的双亲的编号为【i/2】;否则结点i是根结点,无双亲。 (2)如果2i<=n,则 结点i的左孩子的编号为2i;否则结点i无左孩子。 (3)如果2i+1<=n,则结点i的右孩子的编号为2i+1,否则结点i无右孩子。 5.3.3 二叉的抽象数据类型定义 同类似,在不同的应用中,二叉的基本操作不尽相同。 5.3.4 二叉的遍历操作 二叉的遍历是指从根节点出发,按照某种次序访问二叉是所有结点,使得每个结点被访问一次且仅被访问一次。由于二叉中每个结点都可能有两个子,因此需要寻找一条合适的搜索路径。 1、前序遍历 前序遍历二叉操作定义为: 若为空,则空操作返回;否则 (1)访问根结点 (2)前序遍历根结点的左子 (3)前序遍历根结点的右子 2、中序遍历 中序遍历二叉操作定义为: 若为空,则空操作返回;否则 (1)中序遍历根结点的左子 (2)访问根结点 (3)中序遍历根结点的右子 3、后序遍历 后序遍历根结点的左子 后序遍历根结点的右子 访问根结点 4、层序遍历 二叉的层序遍历是指从二叉的第一层开始,从上之下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问。 5.4 二叉存储结构及实现 5.4.1 顺序存储结构 具体步骤: (1)将二叉按完全二叉编号。 (2)将二叉中的结点一编号顺序存储到一维数组中。 5.4.2 二叉链表 基本思想: 令二叉的每个结点对应一个链表结点,链表结点除了存放于二叉结点有关的数据信息外,还要设置指示左右孩子的指针。 5.4.3 三叉链表二叉链表存储方式下,从某个结点出发可以直接访问它的孩子结点,但要找到它的双亲结点,则需要从根节点开始搜索,最坏的情况下,需要遍历整个二叉链表。此时采用三叉链表储存二叉。 其中,data,lchild,rchild三个域的含义同二叉,parent域为指向该结点的双亲结点指针。 5.4.4 线索链表 按照某种遍历次序对二叉进行遍历,可以把二叉中所有结点排成一个线性序列。在集体应用中,有时需要访问二叉中的结点在某种遍历序列中前驱和后继,此时,在存储结构中应该保存结点在某种遍历序列中的前驱和后继信息。 前驱和后继结点的指针称为线索,加上线索的二叉称为线索二叉,加上线索的二叉链表称为线索链表。 5.5 二叉遍历的非递归算法 5.5.1 前序遍历非递归算法 关键:在前序遍历过某个左子后,如何找到该结点的右子的根指针。 一般的前序遍历执行过程中,设要遍历二叉的根指针为bt,可能出现两种情况: (1)若bt!=NULL,则表明当前二叉不为空,此时,应输入根结点bt的值并将bt保存到栈中,准备继续遍历bt的左子。 (2)若bt=NULL,则表明以bt为根指针的二叉遍历完毕,并且bt是栈顶指针所指结点的左子,若栈不空,则应根据栈顶指针所指结点找到待遍历右子的根指针并赋予bt,以继续遍历下去;若栈空,则表明整个二叉遍历完毕。 5.5.2 中序遍历非递归算法 此算法只是需要将前序遍历的非递归算法中输出的语句cout<<bt->data移到bt=s[top--]之后即可。 5.5.3 后序遍历非递归算法 后序遍历的不同在于:结点要出入两次栈,出两次栈,这种情况的含义和处理方法为: (1)第一次出栈:只遍历晚左子,右子尚未遍历,则该结点不出栈,利用栈顶结点找到它的右子,准备遍历它的右子。 (2)第二次出栈:遍历完右子,该结点出栈,并访问它。 设根指针为bt,则可能有以下两种情况: (1)若bt!=NULL,则bt及标志flag入栈,遍历其左子。 (2)若bt=NULL,此时栈空,则整个遍历结束;若栈不空,则表明栈顶结点的左子或右子已遍历结束。若栈顶点的标志flag=1,则表明栈结点的左子已遍历完毕,将flag修改为2,修改为2,并遍历栈定点的右子;若栈顶结点的标志flag=2,则表明栈结点的右子也遍历完毕,输出栈顶结点。 5.6 、森林与二叉的转换 1.转换为二叉 将一棵转换为二叉的方法为: (1)加线——中所有相邻的兄弟结点之间加一条线; (2)去线——对中的每个节点,只保留它与第一个孩子结点之间的连线,删去它与其他孩子结点之间的连线。 (3)层次调节——以根结点为轴心,将顺时针转动一定角度,使之层次分明。 2.森林转换成二叉 (1)将森林中的每一棵二叉转化成二叉; (2)从第二课二叉开始,依次把后一棵二叉的根结点作为一棵二叉根节点的右孩子,当所有二叉连起来后,此时所得到的二叉就是由森林转换得到的二叉。 3、二叉转换为或森林 (1)加线——若某个结点x是其双亲y的左孩子,则把结点x的右孩子、右孩子的右孩子、……,都与结点y用线连起来; (2)去线——删去原二叉中所有的双亲结点与右孩子结点的连线; (3)层次调整——整理由(1)、(2)两步所得到的或森林,使之层次分明。 (4)森林的遍历 两种遍历方法;前序遍历后续遍历。 5.7 应用举例 5.7.1 二叉的应用举例——哈夫曼及哈夫曼编码 1、哈夫曼也称最优二叉,在实际中有着广泛的应用。 叶子节点的权值 是对叶子结点赋予的一个有意义的数值量。 二叉的带权路径长度 设二叉具有n个带权值的叶子节点,从根节点到叶子节点的路径长度与相应的叶子节点权值的乘积之和叫做二叉的带权路径长度,记为: WPL=EWkLk 哈夫曼 给定一组具有确定权值的叶子结点,可以构造出不同的二叉,将其中带权值路径长度最小的二叉称为哈夫曼。 哈夫曼算法基本思想: (1)初始化:由给定的n个权值构造n棵只有一个根结点的二叉,从而得到一个二叉集合。 (2)选取与合并:在F中选取根结点的权值最小的两棵二叉分别作为左、右子构造一棵新的二叉,这棵新的二叉的根结点的权值为其左右子根结点的权值之和。 (3)删除与加入:在F中删除作为左、右子的两棵二叉,并将新建的二叉加入到F中。 (4)重复(2)(3)两步的操作,当集合F只剩下一棵二叉时这棵二叉便是哈夫曼。 2、哈夫曼编码 在进行程序设计时,通常给每一个字符记一个单独的代码来表示一组字符,我们称之为编码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值