二叉树的基本结构以及建树、遍历(递归、非递归)

二叉树的相关问题

//头文件定义

typedef struct BtNode
    {
        BtNode *lchild;
        BtNode *rchild;
        BtNode *pre;
        char data;
    }BtNode;

1、建立一棵二叉树

1> BtNode* CreateTree()   //用#表示为空的孩子,这样就可以找到叶子结点了。
{
    BtNode *s = NULL;
    char item;
    scanf("%c",&item);
    if(item != '#')
    {
        s = BuyNode();
        s->data = item;
        s->lchild = CreateP();
        s->rchild = CreateP();
    }
    return s;
}
2>BtNode* CreateTree3(char * * const str)//这里相当于,(&str)属于引用
{
    BtNode *s = NULL;

    if(**str != '#' && str!=NULL && *str!=NULL)
    {
        s = BuyNode();
        s->data = **str;
        s->lchild = CreateTree3(&(++*str));
        s->rchild = CreateTree3(&(++*str));
    }
    return s;
}


根据两个遍历序列建树,两两组合。
==========

3、根据二叉树的前序遍历串和中序遍历串建树
    思路就是根据二叉树的前序和中序遍历特点,前序遍历的第一个结点就是根节点,然后在中序遍历串里找这个根节点,把中序遍历串分成了两个子串(分别是左子树和右子树),继续用前序遍历的第二个结点,在中序遍历串的左子树子串里面找然后返回下标,又把中序遍历串的左子树子串分成了两个子串(分别是左子树和右子树),以此递归调用,分别生成二叉树的左右子树。

BtNode * Create1(char *ps,char *is,int n)
{
    BtNode *s = NULL;
    if(n > 0)
    {
        s = BuyNode();
        s->data = ps[0];
        int pos = FindIs(is,n,ps[0]);
        if(pos == -1) exit(1);
        s->lchild = Create1(ps+1,is,pos);
        s->rchild = Create1(ps+pos+1,is+pos+1,n-pos-1);
    }
    return s;
}

BtNode* CreatePI(char* ps,char *is)
{
    if(ps == NULL || is == NULL)
    {
        return NULL;
    }else
    {
        int n = strlen(ps);
        return Create1(ps,is,n);
    }
}

4、根据二叉树的中序遍历串和后序遍历串建树
    主要思路就是,后序遍历从后面往前找,也先是根节点,然后去中序遍历串里面找这个节点,返回下标,把中序遍历串分成两个子串(左子树串和右子树串)。
BtNode * Create2(char *is,char *ls,int n)
{
    BtNode *s = NULL;
    if(n > 0)
    {
        int pos = FindIs(is,n,ls[n-1]);
        if(pos == -1) exit(1);
        s = Buynode();
        s->data = ls[n-1];
        s->leftchild = Create2(is,ls,pos);
        s->rightchild = Create2(is+pos+1,ls+pos,n-pos-1);
    }
    return s;
}
BtNode * CreateIL(char *is,char *ls,int n)
{
    if(is == NULL || ls == NULL || n < 1)
        return NULL;
    else
        return Create2(is,ls,n);
}
5、!!!!前序遍历和后序遍历串无法进行建树,因为没有办法找到左右子树。

2、二叉树的遍历

前、中、后序的递归版本

void PreOrder(BtNode *ptr)
{
    if(ptr != NULL)
    {
        printf("%c ",ptr->data);
        PreOrder(ptr->lchild);
        PreOrder(ptr->rchild);
    }
    printf("\n");

}

void InOrder(BtNode *ptr)
{
    if(ptr != NULL)
    {
        InOrder(ptr->lchild);
        printf("%c ",ptr->data);
        InOrder(ptr->rchild);
    }
        printf("\n");

}

void PastOrder(BtNode *ptr)
{
    if(ptr != NULL)
    {
        PastOrder(ptr->lchild);
        PastOrder(ptr->rchild); 
        printf("%c ",ptr->data);
    }
    printf("\n");
}

重点来了,前、中、后序的遍历的非递归版本,中心思想都是用自己的栈代替递归栈帧

前序遍历的非递归版本,先把根节点访问,访问的过程就是出栈顶元素,然后把栈顶元素打印,然后再去把左右结点入栈,再访问。
void NicePerOrder(BtNode *ptr)
{
    stack<BtNode*> mystack;
    BtNode *head;
    mystack.push(ptr);
    while(!mystack.empty())
    {
        head = mystack.top();
        mystack.pop();
        printf("%c,",head->data);
        if(head->rchild!=NULL)
        {
            mystack.push(head->rchild);
        }
        if(head->lchild!=NULL)
        {
            mystack.push(head->lchild);
        }
    }
    printf("\n");
}

中序遍历,一直去找最左边的叶子结点,把一路上的结点都入栈,找到后往回退(出栈),得到栈顶元素,把它的右边入栈,继续找左叶子,注意head结点的切换
void NiceInOrder(BtNode *ptr)
{
    if(ptr == NULL) return ;
    stack<BtNode*> mystack;
    BtNode *head = ptr;
    while(!mystack.empty()|| head!=NULL)
    {
        if(head!=NULL)
        {
            mystack.push(head);
            head = head->lchild;
        }else
        {
            head = mystack.top();
            mystack.pop();
            printf("%c,",head->data);
            head = head->rchild;
        }
    }
}   

后序遍历的非递归版本,这里提供两种方式实现
(1)两个栈,s1,s2,s1做的事情就是头结点入栈,然后出栈,入到s2里面,然后访问左子树,右子树,依次入栈,然后把依次出栈,再入s2里面,这时s2里面从底到顶的顺序就是头、右、左,然后s2的出栈顺序就是后序遍历的顺序。
void NicePastOrder(BtNode *ptr)
{
    stack<BtNode*> s1,s2;
    BtNode *head = ptr;
    s1.push(head);
    while(!s1.empty())
    {
        head = s1.top();
        s1.pop();
        s2.push(head);
        if(head->lchild!=NULL)
        {
            s1.push(head->lchild);
        }
        if(head->rchild!=NULL)
        {
            s2.push(head->rchild);
        }
    }
    while(!s2.empty())
    {
        printf("%c,",s2.top()->data);
        s2.pop();
    }
}

(2)一个栈实现后序遍历:
主要是访问完一个根节点的左子树后再访问右子树,要对右子树有一个标记,防止退回去的时候再进到右子树里面去。

void NicePastOrder2(BtNode *ptr)//用一个栈实现
{
    stack<BtNode*> s;
    BtNode* tag;
    if(ptr == NULL)return;
    while(!s.empty() || ptr!=NULL)
    {
        while(ptr!=NULL)
        {
            s.push(ptr);
            ptr = ptr->lchild;
        }
        ptr = s.top();
        s.pop();
        if(ptr->rchild == NULL || ptr->rchild == tag)
        {
            printf("%c ",ptr->data);
            tag = ptr;
            ptr = NULL;//这句一定不能少,不然ptr就一直是最左边的叶节点;
        }else
        {
            s.push(ptr->rchild);
            ptr = ptr->rchild;
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值