数据结构学习3——树

1 树的结构

1.1 树的逻辑结构

在这里插入图片描述
1) 树的定义与特点

说明
子树、空树、有序树、无序树、森林 空树无结点,有序无序看子树交换顺序是不是同一棵
叶子结点、分支结点、根结点、孩子结点、双亲结点、兄弟结点、堂兄弟结点
结点的度、树的度 度即分叉数,最大的结点的度即树的度
路径长度、祖先、子孙 分支数为路径长度,路径若存在必唯一
结点的层数、树的深度/高度 层数从1开始
结点的层序编号 从上到下、从左到右、从1开始、连续自然数编号

2) 树的遍历
前序遍历:从根结点,从左到右前序遍历子树,ABEFCGHID
后序遍历:从叶子结点,从左到右后序遍历子树,EFBHIGCDA
层序遍历:按层序编号,ABCDEFGHI

1.2 树的存储结构

1) 双亲表示法:每个结点,除自身数据外,还记录双亲结点的下标,方便求解双亲,但求结点的孩子需要遍历。
在这里插入图片描述

2) 孩子链表表示法:每个结点,除自身数据外,还记录一个链表,链表记录从左到右孩子结点的下标,优缺点有双亲表示法相反
在这里插入图片描述

3) 孩子兄弟表示法:每个结点,除自身数据外,还记录第一个孩子结点和右侧的第一个兄弟结点
在这里插入图片描述

1.3 二叉树的逻辑结构

在这里插入图片描述

1) 二叉树的定义与说明

说明
二叉树 左子树、右子树、空二叉树 左右子树互不相交,结点的度最大为2
斜树 左斜树、右斜树 所有分支都只有左子树/右子树,斜树 =》结点个数等于树的深度
满二叉树 所有分支结点都有左右子树,所有叶子结点都在同一层
完全二叉树 按层序编号,倒序从满二叉树中去掉任意数量结点,满二叉树=》完全二叉树

2) 二叉树的性质
3) 二叉树的遍历
前序遍历:从根结点,从左到右前序遍历左右子树,ABDGECF
中序遍历:先遍历根结点的左子树,访问根结点,遍历右子树,DGBEACF ,在遍历子树的过程中,也是按照:先左,然后结点,然后右,(NDGBEANCF)
后序遍历:从叶子结点,从左到右后序遍历左右子树,GDEBFCA
层序遍历:按层序编号,ABCDEFG

1.4 二叉树的存储结构(递归遍历)

1) 顺序存储结构
根据层序编号,使用一维数组存储。适用于完全二叉树,对于非完全二叉树,需要补充空结点生成完全二叉树,存在空间浪费的情况,特别是右斜树。
2) 二叉链表
对比树结构的孩子链表示法,结点使用二叉链表,每个结点包括数据域 和 左右两个指针域指向左右分支结点。
(二叉链表的遍历、构造和析构都可以递归的方式进行,以结点指针为空作为结束标志)
(二叉链表的构造:将二叉树进行扩展,使得原二叉树的结点的度都是2,然后再递归创建)

pLeftChild data pRightChild

在这里插入图片描述

template <class T>
struct BiNode{
    T data;       
    BiNode<T> *lchild, *rchild;
};

//构造
template <class T>
BiNode<T> *BiTree<T>::Creat(BiNode<T> *bt) 
{
    char ch;
    cout<<"请输入创建一棵二叉树的结点数据:"<<endl;
    cin>>ch;
    /*'#'代表空二叉树*/
    if(ch == '#') 
        return NULL;
    else 
    { 
        bt = new BiNode<T>; /*生成新结点*/
        bt->data = ch;
        bt->lchild = Creat(bt->lchild); /*递归创建左子树*/
        bt->rchild = Creat(bt->rchild); /*递归创建右子树*/
    } 
    return bt;
}

//析构
template <class T>
void BiTree<T>::Release(BiNode<T> *bt) 
{
    /*按照后序遍历的顺序释放二叉树*/
    if(bt != NULL) 
    {                  
        Release(bt->lchild); /*释放左子树*/
        Release(bt->rchild); /*释放右子树*/
        delete bt; /*删除根结点*/
    }  
}

//前序遍历
template <class T>
void BiTree<T>::PreOrder(BiNode<T> *bt) 
{
    if(bt == NULL) /*递归调用的边界条件*/
        return;
    else 
    {       
        cout<<bt->data<<" "; /*访问根结点*/
        PreOrder(bt->lchild); /*前序递归遍历左子树*/
        PreOrder(bt->rchild); /*前序递归遍历右子树*/
    }
}

//中序遍历
template <class T>
void BiTree<T>::InOrder(BiNode<T> *bt) 
{
    if(bt == NULL)  
        return; /*递归调用的边界条件*/             
    else 
    {   
        InOrder(bt->lchild); /*中序递归遍历左子树*/
        cout<<bt->data<<" "; /*访问根结点*/
        InOrder(bt->rchild); /*中序递归遍历的右子树*/
    }
}

//后序遍历
template <class T>
void BiTree<T>::PostOrder(BiNode<T> *bt) 
{
    if(bt == NULL)  
        return; /*递归调用的边界条件*/
    else 
    {   
        PostOrder(bt->lchild); /*后序递归遍历左子树*/
        PostOrder(bt->rchild); /*后序递归遍历右子树*/
        cout<<bt->data<<" "; /*访问根结点*/
    }
}

//层序遍历
template <class T>
void BiTree<T>::LevelOrder(BiNode<T> *bt)
{
    BiNode<T> *q;
    queue<BiNode<T> *> Q;
    if(bt == NULL) 
        return;
    else 
    {
        Q.push(bt); /*bt入队*/
        /*队列非空时循环*/
        while(Q.empty() != 1) 
        {
            q = Q.pop(); /*队头出队*/
            cout<<q->data<<" "; /*访问队头*/    
            if(q->lchild != NULL) /*如果队头有左孩子,左孩子入队*/
                Q.push(q->lchild);      
            if(q->rchild != NULL) /*如果队头有右孩子,右孩子入队*/
                Q.push(q->rchild);
        }
    }
}

3) 三叉链表
在二叉链表的基础上,增加指向双亲结点的指针。

pLeftChild data pRightChild pParent

在这里插入图片描述

2 二叉树应用

2.1 非递归遍历二叉树

//前序
template <class T>
void BiTree<T>::preOrder(BiNode<T> *bt) 
{
    stack<BiNode<T> *> S;//使用栈保存访问过的结点
    while(bt != NULL || S.empty() != 1)
    {
        while(bt != NULL)
        {
            cout<<bt->data<<endl;
            S.push(bt);
            bt = bt->lchild;
        }
        if(S.empty() != 1)
        {
            bt = S.pop();
            bt = bt->rchild;
        }
    }
}

//中序
template <class T>
void BiTree<T>::inOrder(BiNode<T> *bt) 
{
    stack<BiNode<T> *> S;//使用栈保存访问过的结点
    while(bt != NULL || S.empty() != 1)
    {
        while(bt != NULL)   //bt == NULL 说明左子树为空,或左子树已访问完
        {
            S.push(bt);
            bt = bt->lchild;
        }
        if(S.empty() != 1)
        {
            bt = S.pop();
            cout<<bt->data<<endl;//先打印左子树,再打印结点
            bt = bt->rchild;
        }
    }
}

//后序1:使用标志位,给结点绑定一个flag,访问完左子树,flag为1,左右子树都访问完后,flag为2
template <class T>
void BiTree<T>::postOrder(BiNode<T> *bt) 
{
    stack<Element> S;
    Element e;
    /*bt不为空或者栈不为空*/
    while(bt != NULL || S.Empty() == 0) 
    {
        while(bt != NULL) 
        {
            e.ptr = bt;
            e.flag = 1;
            S.posh(e);
            bt = bt->lchild;
        }
        /*栈不为空并且栈顶的flag为2时,出栈并访问*/
        while((S.empty() != 1)&&(S.top()).flag == 2) 
        {
            e = S.Pop();
            cout<<e.ptr->data<<" ";
        }
        /*栈不为空,并且栈顶的flag为1时,将栈顶的flag更改为2,并访问其右孩子*/
        if(S.empty() != 1) 
        {
            e = S.pop();
            bt = e.ptr->rchild;
            e.flag = 2;
            S.push(e);
        }
    }
}

//后序2:不使用标志位
template <class T>
void BiTree<T>::postOrder(BiNode<T> *bt) 
{
    stack<BiNode<T> *> s;
    stack<BiNode<T> *> output;
    s.push(bt);

    while (!s.empty()) 
    {
        bt = s.pop();
        output.push(bt);

        if (bt->lchild)
            s.push(bt->lchild);
        if (bt->rchild)
            s.push(bt->rchild);
    }

    while (!output.empty()) 
    {
        bt = output.pop();
        cout << bt->data << " ";
    }
}

2.2 重建二叉树

//由前序序列和中序序列
template <class T>
BiNode<T>* BiTree<T>::Rebuild(T *preOrder, T *inOrder, int n) 
{
    if(n == 0) 
        return NULL;

    T c = preOrder[0];/*获得前序遍历的第一个结点*/ 
    BiNode<T> *node = new BiNode<T >;/*创建根结点*/
    node->data = c;
    node->lchild = NULL;
    node->rchild = NULL;

    int i;
    for(i = 0; i < n && inOrder[i] != c; i++)/*在中序遍历序列中寻找根结点的位置*/
        ;

    int lenLeft = i;/*左子树结点个数*/ 
    int lenRight = n - i - 1; /*右子树结点个数*/

    if(lenLeft > 0)/*左子树不为空,递归重建左子树*/ 
        node->lchild = Rebuild(&preOrder[1], &inOrder[0], lenLeft);

    if(lenRight > 0) /*右子树不为空,递归重建右子树*/
        node->rchild = Rebuild(&preOrder[lenLeft+1], &inOrder[lenLeft+1], lenRight);

    return node;
}

//由后序序列和中序序列
template <class T>
BiNode<T>* BiTree<T>::Rebuild(T *postOrder, T *inOrder, int n) 
{
    if(n == 0) 
        return NULL;

    T c = postOrder[n-1];/*获得后序遍历的最后一个结点*/ 
    BiNode<T> *node = new BiNode<T>; /*创建根结点*/
    node->data = c;
    node->lchild = NULL;
    node->rchild = NULL;

    int i;
    for(i = 0; i < n && inOrder[i] != c; i++)/*在中序遍历序列中寻找根结点的位置*/
        ;

    int lenLeft = i;/*左子树结点个数*/
    int lenRight = n - i - 1; /*右子树结点个数*/

    if(lenLeft > 0)/*左子树不为空,重建左子树*/ 
        node->lchild = Rebuild(&postOrder[0], &inOrder[0], lenLeft);

    if(lenRight > 0)/*右子树不为空,重建右子树*/
        node->rchild = Rebuild(&postOrder[lenLeft], &inOrder[lenLeft+1], lenRight);
    return node;
}

3 二叉搜索树 / 堆 / 红黑树

系列参考

1) 《数据结构(C++)边学边做》任平红等著
2) 《剑指offer》何海涛著
3) http://bookshadow.com/weblog/2015/01/19/binary-tree-post-order-traversal/

Leave a Reply

Your email address will not be published. Required fields are marked *