1.树的建立与遍历(模板)

原贴:https://www.cnblogs.com/liuamin/p/6269950.html

一、总体源代码:

#include<iostream>
#include<stdlib.h>
using namespace std;

typedef char ElemType;

//二叉树的二叉链表结构,也就是二叉树的存储结构,1个数据域,2个指针域(分别指向左右孩子)

typedef  struct BiTNode
{
    ElemType data;
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

//二叉树的建立,按前序遍历的方式建立二叉树,当然也可以以中序或后序的方式建立二叉树
void CreateBiTree(BiTree *T)
{
    ElemType ch;
    cin >> ch;
    if (ch == '#')
        *T = NULL;  //保证是叶结点
    else
    {
        *T = (BiTree)malloc(sizeof(BiTNode));
        //if (!*T)
            //exit(OVERFLOW); //内存分配失败则退出。
        (*T)->data = ch;//生成结点
        CreateBiTree(&((*T)->lchild));//构造左子树
        CreateBiTree(&(*T)->rchild);//构造右子树    
    }
}
//表示对遍历到的结点数据进行的处理操作,此处操作是将树结点前序遍历输出
void operation1(ElemType ch)
{
    cout << ch << " ";
}
//此处在输出的基础上,并输出层数
void operation2(ElemType ch, int level)
{
       cout << ch << "在第" << level << "层" << endl;
}


//递归方式前序遍历二叉树
void PreOrderTraverse(BiTree T, int level)
{
    if (T == NULL)
        return;
/*此处表示对遍历的树结点进行的操作,根据你自己的要求进行操作,这里只是输出了结点的数据*/
    //operation1(T->data);
    operation2(T->data, level); //输出了层数

    PreOrderTraverse(T->lchild, level + 1);
    PreOrderTraverse(T->rchild, level + 1);
}

//递归方式中序遍历二叉树

void InOrderTraverse(BiTree T,int level)
{
if(T==NULL)
return;
InOrderTraverse(T->lchild,level+1);

//operation1(T->data);
operation2(T->data, level); //输出了层数

InOrderTraverse(T->rchild,level+1);
}

//递归方式后序遍历二叉树

void PostOrderTraverse(BiTree T,int level)
{
if(T==NULL)
return;
PostOrderTraverse(T->lchild,level+1);
PostOrderTraverse(T->rchild,level+1);

//operation1(T->data);
operation2(T->data, level); //输出了层数
}


int main()
{
    int level = 1; //表示层数
    BiTree T = NULL;
    cout << "请以前序遍历的方式输入扩展二叉树:"; //类似输入AB#D##C##
    CreateBiTree(&T);// 建立二叉树,没有树,怎么遍历

    cout << "递归前序遍历输出为:" << endl;
    PreOrderTraverse(T, level);//进行前序遍历,其中operation1()和operation2()函数表示对遍历的结点数据进行的处理操作
    cout << endl;

    cout << "递归中序遍历输出为:" << endl;
    InOrderTraverse(T, level);
    cout << endl;

    cout << "递归后序遍历输出为:" << endl;
    PostOrderTraverse(T, level);
    cout << endl;

    return 0;
}

二、树的结构:

typedef  struct BiTNode
{
    ElemType data;
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

心得:

    1、树的结构其实是一个结构体的嵌套,每一个节点的左右孩子都是父节点的两个指针域,这样子在用链表实现时就避免了链接的过程;

    2、在定义树结构时,可以直接在struct前+typedef,在结构尾部可以写多个参数,不带*的是替换的节点名称(默认是不变的,为了替换节点指针好像必须写上),带*的是替换的节点指针名称,这样定义的结构在程序中依然存在;

三、树的建立部分:

//二叉树的建立,按前序遍历的方式建立二叉树,当然也可以以中序或后序的方式建立二叉树
void CreateBiTree(BiTree *T)
{
    ElemType ch;
    cin >> ch;
    if (ch == '#')
        *T = NULL;  //保证是叶结点
    else
    {
        *T = (BiTree)malloc(sizeof(BiTNode));
        //if (!*T)
            //exit(OVERFLOW); //内存分配失败则退出。
        (*T)->data = ch;//生成结点
        CreateBiTree(&((*T)->lchild));//构造左子树
        CreateBiTree(&(*T)->rchild);//构造右子树    
    }
}

心得:

整个流程(前序建立):

1.定义参数 BiTree *T,这里的BiTree是结构体指针,所以T是指针的指针,之所以要这样定义是为了后面遍历时方便,取指针为*T,取左孩子是(*T)->lchild,将左孩子传参则是    &((*T)->lchild  )因为&的优先级较->低,所以可以省略一个括号:

&(*T)->lchild    ;

2.ET(ElemType) ch,并对ch赋值

3.如果ch==‘#’(这里默认#是空节点),对*T赋空,意味着本节点不存入树中

4.如果非空,则(开辟与赋值)该节点,统称(生成)该节点

5.以递归方式进行左右孩子的传参,参数格式1中已写到;

四、二叉树的遍历

1、递归版:

//递归方式前序遍历二叉树
void PreOrderTraverse(BiTree T, int level)
{
    if (T == NULL)
        return;
/*此处表示对遍历的树结点进行的操作,根据你自己的要求进行操作,这里只是输出了结点的数据*/
    //operation1(T->data);
    operation2(T->data, level); //输出了层数

    PreOrderTraverse(T->lchild, level + 1);
    PreOrderTraverse(T->rchild, level + 1);
}
//此处在输出的基础上,并输出层数
void operation2(ElemType ch, int level)
{
       cout << ch << "在第" << level << "层" << endl;
}

非递归的写法很简单,前序和中序还有后续的区别就是输出语句的位置不同

(前)左递归(中)右递归(后)

在递归首部要有判断来测空

    if (T == NULL)
        return;

2、非递归版:堆栈实现版

(先序与中序):

树结构:abd##fe###cg#h##i##

自程:

代码:

void  xianxu(BiTree T)
{
    stack<BiTree>s;
    while(T||!s.empty())//栈不空 
    {
        while(T)
        {
            s.push(T);
            cout<<T->data<<" "; 
            T=T->lchild;
        }
        if(!s.empty())
        {
            T=s.top();
            s.pop();
            T=T->rchild;
        }
    }
}
void zhongxu(BiTree T)
{
    stack<BiTree>s;
    while(T||!s.empty())//栈不空 
    {
        while(T)
        {
            s.push(T);
         // cout<<T->data<<" ";  前序遍历时输出语句在这
            T=T->lchild;
        }
        if(!s.empty())
        {
            T=s.top();
            s.pop();
            cout<<T->data<<" "; 
            T=T->rchild;
        }
    }
}

重点:

0、整个遍历的流程:

遍历顺序默认是前序,遍历时将节点指针压入栈中;

先遍历根节点,再以根节点的左子树为根节点依次遍历,当左子树遍历完毕时,弹出当前栈顶元素,并将指针指向该元素的右子树,然后继续左子树遍历,如此循环,直到遍历结束整棵树(标志:栈为空且指针指向Null)

1、前序与中序的区别就是输出语句的位置问题,在这种写法中,每一个节点其实是在程序中出现三次的,第一次是遍历时压入栈中,第二次是弹出该节点时,第三次是将指针指向该节点的右子树时。在压入栈时输出访问节点就是前序遍历,在弹出时访问该节点就是中序遍历,而后序遍历是无法通过改变输出语句完成的,因为在第二次时,已经将该节点从栈中弹出,无法再次访问,所以完成后序遍历需要其他的手段;

后序遍历:

    typedef struct TreeNode * BinTree;
    struct TreeNode {
        ElementType Data;
        BinTree Left;
        BinTree Right;
        int flag;             //结点的结构中加了一个flag,用于标记是否被访问过
    };
     
     
    void PostorderTraversal (BinTree BT)
    {
        BinTree T = BT;
        Stack S = CreatStack();
         
        while (T || !IsEmpty(S))
        {
            while ( T && T -> flag == 0 )   
            //在结点里加了一个标记flag用于判断是否访问过,0表示未访问过,1表示访问过。若已经访问过,再次遇到就不再进入这个循环 
            {
                Push (S, T);
                T -> flag = 1;               
                T = T -> left;
            }
         
            T = Pop(S);
            if ( T -> right && T -> right -> flag == 0 )   //若有右儿子且未访问过,再次压入栈 
                Push ( S , T );
            else
                printf ( "%d" , T -> Data );
            T = T -> right;
        }
    }

重点:后序遍历的操作之一就是标记法,在创造节点时多增加一个flag属性,并赋0,对每个节点进行多次入栈判断,如果无右儿子则不会重复进栈,有右儿子则再次进栈,因为后续遍历的根节点是最后访问,所以需要再次进栈等待访问。


总结:不管怎么说磕磕绊绊的也算把二叉树的第一节给滚过来了,开心(*^▽^*)!

猜你喜欢

转载自blog.csdn.net/qq_41660465/article/details/79942001
今日推荐