王道数据结构实战ch5二叉树

王道数据结构实战ch5二叉树

  • 二叉树层次建树
    • 结构体定义
      • 二叉树节点的定义
      • 辅助队列的节点定义
  • 二叉树的遍历
    • 二叉树递归先序遍历
    • 二叉树递归中序遍历
    • 二叉树递归后序遍历
    • 二叉树非递归中序遍历
    • 二叉树按层次遍历(BFS)
    • 完整代码
  • 线索二叉树
    • 结构体定义
    • 完整代码

二叉树层次建树

定义两种结构体:
树的节点
(数据域:字符 )
(指针域:指向自身类型的左指针,指向自身类型的右指针)
列队的节点
(数据域:指向树的节点的指针 )
( 指针域:指向自身类型的指针)

结构体定义

二叉树节点的定义

typedef struct BiTNode  //树的节点
{
    ElemType c;
    struct BiTNode *lchild;
    struct BiTNode *rchild;
} BiTNode,*BiTree;

辅助队列的节点定义

为了节省空间,辅助队列的节点,数据域只需要存放树某个节点的指针,而不需要存放完整的树节点

typedef struct tag   //辅助链队的节点,为了节省空间,数据域存放的是树某个节点的指针!!
{
    BiTNode* p;    //指向树节点类型的指针
    struct tag* pnext;  //指向自身类型的指针
} tag_t,*ptag_t;

1.首先第一个节点为根节点,入队
2.从第二个节点开始,首先入队。依次判断当前队头元素的左、右儿子是否为空,空就成为其左或右儿子,两个儿子都满了,辅助队列的遍历指针向后移动。

  malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。
BiTree pnew;  //临时指向新的节点的工作指针,后面会赋值给树
    char c;  //暂存数据
    BiTree tree=NULL;  //树根
    ptag_t phead=NULL;
    ptag_t ptail=NULL; //辅助队列头
    ptag_t listpnew=NULL;  //ptail辅助队列尾
    ptag_t pcur=NULL;
    //abcdefghij

    while(scanf("%c",&c)!=EOF)  //对于每个输入的元素实时生成节点并加入树
    {
        if(c=='\n') break;  //回车结束输入

//树的节点
        pnew=(BiTree)calloc(1,sizeof(BiTree));   //创建一个新的树的节点,并赋值为0
        pnew->c=c;  //树的节点放入当前输入的数据
//队列的节点
        listpnew=(ptag_t)calloc(1,sizeof(tag_t));   //创建(申请空间)辅助队列节点
        listpnew->p=pnew;   //队列节点存放的指向新节点 的指针

        if(tree==NULL)    //树的第一个节点加入
        {
            tree=pnew;       //把树的新节点赋给树根
            phead=listpnew;     //辅助队列节点入队:辅助队列头指针,指向新的辅助队列节点
            ptail=listpnew;       //辅助队列尾指针,指向新的辅助队列节点
            pcur=listpnew;    //游标用来遍历队列,永远指向树中要插入位置的父节点
            continue;   //处理第二个和以后树的节点
        }

        else   //当前不是第一个节点,加入已经存在的树
        {
            ptail->pnext= listpnew; //用尾插法把树的新节点加入辅助队列
            ptail=listpnew;
        }

        //把新节点放到已有的树中
        if(pcur->p->lchild==NULL)   //判断树现在节点有没有左儿子
        {
            pcur->p->lchild=pnew;   //在树现节点的左边插入新节点
        }
        else if(pcur->p->rchild==NULL)  //如果树当前节点已经有左儿子,判断树现在节点有没有右儿子
        {
            pcur->p->rchild=pnew;  //在树现节点的右边插入新节点
            pcur=pcur->pnext; //!!有了右儿子,树这个节点已满,移动到辅助队列中下一个树节点
        }
    }

二叉树的遍历

二叉树递归先序遍历

void preOrder(BiTree T)  //递归前序遍历(DFS)
{
    if(p!=NULL)
    {
        putchar(p->c);
        preOrder(p->lchild);
        preOrder(p->rchild);
    }
}

二叉树递归中序遍历

void inOrder(BiTree T)  //递归中序遍历
{
    if(p!=NULL)
    {
        inOrder(p->lchild);
        putchar(p->c);
        inOrder(p->rchild);
    }
}

二叉树递归后序遍历

void postOrder(BiTree T)  //递归后序遍历
{
    if(p!=NULL)
    {
        postOrder(p->lchild);
        postOrder(p->rchild);
        putchar(p->c);
    }
}

二叉树非递归中序遍历

使用了用户栈,相比递归遍历,效率提高了

  1. 从根点开始,压栈,并取左孩子不断压栈,直到没有左孩子。
  2. 此时,弹栈打印(最深的左孩子,其实也可以看成此时没有左孩子的子树的根),并取当前节点的右子树给p,对于p,和前面相同操作。
  3. 此时这课子树打印完成。弹栈得到上一级子树的根,迭代重复此过程。
void inOrder_NonRecursion(BiTree T)   //非递归中序遍历,使用栈存放指向树节点的指针
{
    if(T!=NULL)
    {
    stack<BiTree> S;
    BiTree p=T;     //指向树根的指针

    while(p|| !S.empty())  //直到最右边的右儿子为空,且栈空,结束遍历
    {
        if(p)              //当前节点不为空,先不断让当前节点入栈,并取当前节点的左孩子继续入栈,知道没有左孩子
        {
            S.push(p);  //当前节点压栈,为后面能找到上一级的子树做准备
            p=p->lchild;  //p更新为当前节点的左孩子
        }
        else            //直到没有左孩子,弹出栈中元素并打印,然后获取该元素的右节点
        {
            p=S.top();     //取出当前最左下的元素
            S.pop( );  
            putchar(p->c);  //弹栈并打印(访问当前子树的,中间节点)
            
            p=p->rchild;     //访问右子树,直到没有右孩子
        }
    }
    }
}

二叉树按层次遍历(BFS)

  1. 首先把树根入队
  2. (队列非空时)循环,取队头打印,并把左右儿子入队
void levelOrder(BiTree T)    //按层次遍历(BFS),使用队列
{
    queue<BiTree> Q;    //建立队列
    BiTree p;      //二叉树节点类型的工作指针
    
    Q.push(T);  //树根入队
    
    while(!Q.empty())
    {
        p=Q.front();
        Q.pop();   //取队头节点并打印
        putchar(p->c);

        if(p->lchild!=NULL)
            Q.push(p->lchild);
        if(p->rchild!=NULL)
            Q.push(p->rchild);
    }
}

完整代码

#include
using namespace std;

typedef char ElemType;

typedef struct BiTNode  //树的节点
{
    ElemType c;
    struct BiTNode *lchild;
    struct BiTNode *rchild;
} BiTNode,*BiTree;

typedef struct tag   //辅助链队的节点,为了节省空间,数据域存放的是树某个节点的指针!!
{
    BiTNode* p;    //指向树节点类型的指针
    struct tag* pnext;  //指向自身类型的指针
} tag_t,*ptag_t;

void preOrder(BiTree p)  //递归前序遍历(DFS)
{
    if(p!=NULL)
    {
        putchar(p->c);
        preOrder(p->lchild);
        preOrder(p->rchild);
    }
}

void inOrder(BiTree p)  //递归中序遍历
{
    if(p!=NULL)
    {
        inOrder(p->lchild);
        putchar(p->c);
        inOrder(p->rchild);
    }
}

void postOrder(BiTree p)  //递归后序遍历
{
    if(p!=NULL)
    {
        postOrder(p->lchild);
        postOrder(p->rchild);
        putchar(p->c);
    }
}

void preOrder_NonRecursion(BiTree T)  //非递归先序遍历,使用栈存放指向树节点的指针
{
    if(T!=NULL)
    {
        stack<BiTree> S;
        BiTree p;
        S.push(T);
        while(!S.empty())
        {
            p=S.top();
            S.pop();
            putchar(p->c);  //弹栈并打印

            if(p->rchild!=NULL)
                S.push(p->rchild);
            if(p->lchild!=NULL)
                S.push(p->lchild);
        }
    }
}

void inOrder_NonRecursion(BiTree T)   //非递归中序遍历,使用栈存放指向树节点的指针
{
    if(T!=NULL)
    {
        stack<BiTree> S;
        BiTree p=T;     //遍历指针,首先指向树根

        while(p|| !S.empty())  //直到最右边的右儿子为空,且栈空,结束遍历
        {
            if(p)              //一路向左压栈
            {
                S.push(p);  //当前节点压栈
                p=p->lchild;  //左孩子不为空,一路向左
            }
            else            //直到没有左孩子,弹出栈中元素并打印,然后转向出栈元素的右子树
            {
                p=S.top();     //取出当前最左下的元素
                S.pop( );
                putchar(p->c);  //弹栈并打印(访问当前子树的,中间节点)

                p=p->rchild;     //向右子树走,对于右子树,从while循环再开始一轮
            }
        }
    }
}

void  postOrder_NonRecursion(BiTree T)//非递归后序遍历,使用两个栈
{
    if(T!=NULL)
    {
        stack<BiTree> S1,S2;
        S1.push(T);
        BiTree p=NULL;
        while(!S1.empty())
        {
            p=S1.top();
            S1.pop( );

            S2.push(p);
            if(p->lchild!=NULL)
                S1.push(p->lchild);
            if(p->rchild!=NULL)
                S1.push(p->rchild);

        }
        while(!S2.empty())
        {
            p=S2.top();
            S2.pop( );
            putchar(p->c);  //弹栈并打印
        }
    }
}

void levelOrder(BiTree T)    //按层次遍历(BFS),使用队列
{
    queue<BiTree> Q;    //建立队列
    BiTree p;      //二叉树节点类型的工作指针

    Q.push(T);  //树根入队

    while(!Q.empty())
    {
        p=Q.front();
        Q.pop();   //取队头节点并打印
        putchar(p->c);

        if(p->lchild!=NULL)
            Q.push(p->lchild);
        if(p->rchild!=NULL)
            Q.push(p->rchild);
    }
}

int main()
{
    BiTree pnew;  //临时指向新的节点的工作指针,后面会赋值给树
    char c;  //暂存数据
    BiTree tree=NULL;  //树根
    ptag_t phead=NULL;
    ptag_t ptail=NULL; //辅助队列头
    ptag_t listpnew=NULL;  //ptail辅助队列尾
    ptag_t pcur=NULL;
    //abcdefghij

    while(scanf("%c",&c)!=EOF)  //对于每个输入的元素实时生成节点并加入树
    {
        if(c=='\n') break;  //回车结束输入

//树的节点
        pnew=(BiTree)calloc(1,sizeof(BiTree));   //创建一个新的树的节点,并赋值为0
        pnew->c=c;  //树的节点放入当前输入的数据
//队列的节点
        listpnew=(ptag_t)calloc(1,sizeof(tag_t));   //创建(申请空间)辅助队列节点
        listpnew->p=pnew;   //队列节点存放的指向新节点 的指针

        if(tree==NULL)    //树的第一个节点加入
        {
            tree=pnew;       //把树的新节点赋给树根
            phead=listpnew;     //辅助队列节点入队:辅助队列头指针,指向新的辅助队列节点
            ptail=listpnew;       //辅助队列尾指针,指向新的辅助队列节点
            pcur=listpnew;    //游标用来遍历队列,永远指向树中要插入位置的父节点
            continue;   //处理第二个和以后树的节点
        }

        else   //当前不是第一个节点,加入已经存在的树
        {
            ptail->pnext= listpnew; //用尾插法把树的新节点加入辅助队列
            ptail=listpnew;
        }

        //把新节点放到已有的树中
        if(pcur->p->lchild==NULL)   //判断树现在节点有没有左儿子
        {
            pcur->p->lchild=pnew;   //在树现节点的左边插入新节点
        }
        else if(pcur->p->rchild==NULL)  //如果树当前节点已经有左儿子,判断树现在节点有没有右儿子
        {
            pcur->p->rchild=pnew;  //在树现节点的右边插入新节点
            pcur=pcur->pnext; //!!有了右儿子,树这个节点已满,移动到辅助队列中下一个树节点
        }
    }

    cout<<endl<<"----------前序序列----------"<<endl;
    preOrder(tree);

    cout<<endl<<"----------非递归前序序列----------"<<endl;
    preOrder_NonRecursion(tree);

    cout<<endl<<"----------中序序列----------"<<endl;
    inOrder(tree);

    cout<<endl<<"----------非递归中序序列----------"<<endl;
    inOrder_NonRecursion(tree);

    cout<<endl<<"----------后序序列----------"<<endl;
    postOrder(tree);

    cout<<endl<<"----------非递归后序序列----------"<<endl;
    postOrder_NonRecursion(tree);

    cout<<endl<<"----------按层次遍历----------"<<endl;
    levelOrder(tree);

    return 0;
}

线索二叉树

结构体定义

先比与普通二叉树,多了两个标记ltag和rtag

typedef struct ThreadNode
{
    ElemType c;
    struct ThreadNode *lchild;
    struct ThreadNode *rchild;
    int ltag,rtag;  //记录指针指向的是谁,0代表指向孩子,1代表指某种序列的向前驱或后继
} ThreadNode,*ThreadTree;

完整代码

#include
using namespace std;

typedef char ElemType;

typedef struct ThreadNode
{
    ElemType c;
    struct ThreadNode *lchild;
    struct ThreadNode *rchild;
    int ltag,rtag;  //记录指针指向的是谁,0代表指向孩子,1代表指某种序列的向前驱或后继
} ThreadNode,*ThreadTree;

void BuildThreadTree(ThreadTree &T)
{
    ThreadNode* array[5];
    for(int i=0; i<5; i++)
    {
        array[i]=(ThreadNode*)malloc(sizeof(ThreadNode));
        memset(array[i],0,sizeof(ThreadNode*));
        array[i]->c='A'+i;
    }
    T=array[0];
    array[0]->lchild=array[1];
    array[0]->rchild=array[2];
    array[1]->lchild=array[3];
    array[2]->rchild=array[4];
}

void InThread(ThreadTree p,ThreadTree &pre)    //pre记忆了当前节点的前一个节点,两层加引用,会改变全局变量;
{
    if(p!=NULL)
    {
        InThread(p->lchild,pre);  //递归找最边左孩子,也就是当前树中序遍历的第一个
        if(p->lchild==NULL)      //左指针为NULL
        {
            p->lchild=pre;          //左指针指向前驱
            p->ltag=1;         //线索化标记置1
        }
        if(pre!=NULL&&pre->rchild==NULL)   //pre节点右孩子为NULL
        {
            pre->rchild=p;   //左指针指向后继
            pre->rtag=1;   //线索化标记置1
        }

        pre=p;      //p成为当前节点
        InThread(p->rchild,pre);  //处理右子树
    }
}
void CreatInThread(ThreadTree T)
{

    ThreadNode* pre=NULL; //工作指针
    if(T!=NULL)
    {
        InThread(T,pre);
        pre->rchild=NULL;
        pre->rtag=1;
    }
}

ThreadNode* FirstNode(ThreadNode* p)
{
    while(p->ltag==0)
        p=p->lchild;
    return p;
}

int main()
{
    ThreadTree T;  //树根
    ThreadTree p; //接收用的变量
    BuildThreadTree(T);   //建立无线索的树
    //CreatInThread(T);  //加上线索
    p=FirstNode(T);
    cout<<"the first node is:"<<p->c<<endl;
    return 0;
}

你可能感兴趣的:(竞赛+考研,王道数据结构源码实战,数据结构,算法,链表)