zoukankan      html  css  js  c++  java
  • 二叉树的二叉链表存储结构及C++实现

    前言:存储二叉树的关键是如何表示结点之间的逻辑关系,也就是双亲和孩子之间的关系。在具体应用中,可能要求从任一结点能直接访问到它的孩子。

    一、二叉链表

      二叉树一般多采用二叉链表(binary linked list)存储,其基本思想是:令二叉树的每一个结点对应一个链表结点链表结点除了存放与二叉树结点有关的数据信息外,还要设置指示左右孩子的指针。二叉链表的结点结构如下图所示:

    二叉树结点结构
    lchild data rchild

    其中,data为数据域,存放该结点的数据信息;

    lchild为左指针域,存放指向左孩子的指针,当左孩子不存在时为空指针;

    rchild为右指针域,存放指向右孩子的指针,当右孩子不存在时为空指针;

      可以用C++语言中的结构体类型描述二叉链表的结点,由于二叉链表的结点类型不确定,所以采用C++的模板机制。如下:

    1 // 二叉链表的节点
    2 template<class T>
    3 struct BiNode
    4 {
    5     T data;                            // 数据域
    6     BiNode<T>*lchild, *rchild;    // 左右指针域
    7 };        

     二、C++实现

      将二叉树的二叉链表存储结构用C++的类实现。为了避免类的调用者访问BiTree类的私有变量root,在构造函数、析构函数以及遍历函数中调用了相应的私有函数。

    具体代码实现如下:

    1、头文件“cirqueue.h”

    此头文件为队列的类实现,层序遍历要用到队列,所以自己定义了一个队列。

     1 #pragma once
     2 #include <iostream>
     3 const int queueSize = 100;
     4 template<class T>
     5 class queue
     6 {
     7 public:
     8         ....
     9     T data[queueSize];
    10     int front, rear;
    11         ....
    12 };

    2、头文件“bitree.h”

    此头文件为二叉链表的类实现。

    #pragma once
    #include <iostream>
    #include "cirqueue.h"
    // 二叉链表的节点
    template<class T>
    struct BiNode
    {
        T data;    // 数据域
        BiNode<T>*lchild, *rchild;    // 左右指针域
    };
    // 二叉链表类实现
    template<class T>
    class BiTree
    {
    public:
        BiTree() { root = Creat(root); }        // 构造函数,建立一颗二叉树
        ~BiTree() { Release(root); }            // 析构函数,释放各节点的存储空间
        void PreOrder() { PreOrder(root); }      // 递归前序遍历二叉树
        void InOrder() { InOrder(root); }        // 递归中序遍历二叉树
        void PostOrder() { PostOrder(root); }    // 递归后序遍历二叉树
        void LeverOrder();                       // 层序遍历二叉树
    private:
        BiNode<T>* root;                        // 指向根节点的头节点
        BiNode<T>* Creat(BiNode<T>* bt);        // 构造函数调用
        void Release(BiNode<T>* bt);            // 析构函数调用
        void PreOrder(BiNode<T>* bt);           // 前序遍历函数调用
        void InOrder(BiNode<T>* bt);            // 中序遍历函数调用
        void PostOrder(BiNode<T>* bt);          // 后序遍历函数调用
    };
    
    template<class T>
    inline void BiTree<T>::LeverOrder()
    {
        queue<BiNode<T>*> Q;        // 定义一个队列
        Q.front = Q.rear = -1;     // 顺序队列
        if (root == NULL)
            return;
        Q.data[++Q.rear] = root;  // 根指针入队
        while (Q.front != Q.rear)
        {
            BiNode<T>* q = Q.data[++Q.front];    // 出队
            cout << q->data;
            if (q->lchild != NULL)
                Q.data[++Q.rear] = q->lchild;    // 左孩子入队
            if (q->rchild != NULL)
                Q.data[++Q.rear] = q->rchild;    // 右孩子入队
        }
        
    }
    
    template<class T>
    inline BiNode<T>* BiTree<T>::Creat(BiNode<T>* bt)
    {
        T ch;
        cin >> ch;                  // 输入结点的数据信息,假设为字符
        if (ch == '#')                // 建立一棵空树
            bt = NULL;
        else
        {
            bt = new BiNode<T>;          // 生成一个结点,数据域为ch
            bt->data = ch;
            bt->lchild = Creat(bt->lchild);   // 递归建立左子树
            bt->rchild = Creat(bt->rchild);   // 递归建立右子树
        }
        return bt;
    }
    
    template<class T>
    inline void BiTree<T>::Release(BiNode<T>* bt)
    {
        if (bt != NULL)
        {
            Release(bt->lchild);          // 释放左子树
            Release(bt->rchild);          // 释放右子树
            delete bt;                // 释放根节点
        }
    }
    
    template<class T>
    inline void BiTree<T>::PreOrder(BiNode<T>* bt)
    {
        if (bt == NULL)              // 递归调用的结束条件
            return;
        cout << bt->data;             // 访问根节点bt的数据域
        PreOrder(bt->lchild);           // 前序递归遍历bt的左子树
        PreOrder(bt->rchild);           // 前序递归遍历bt的右子树
    }
    
    template<class T>
    inline void BiTree<T>::InOrder(BiNode<T>* bt)
    {
        if (bt == NULL)
            return;
        InOrder(bt->lchild);
        cout << bt->data;
        InOrder(bt->rchild);
    }
    
    template<class T>
    inline void BiTree<T>::PostOrder(BiNode<T>* bt)
    {
        if (bt == NULL)
            return;
        PostOrder(bt->lchild);
        PostOrder(bt->rchild);
        cout << bt->data;
    }

    说明:1、除了层序遍历,其他遍历均为递归算法。

       2、为什么层序遍历使用队列:在进行层序遍历时,对某一层的结点访问完后,再按照它们的访问次序对各个结点的左孩子和右孩子顺序访问,这样一层一层进行,先访问的结点其左右孩子也要先访问,这符合队列的操作特性,因此,在进行层序遍历时,可设置一个队列存放已访问的结点。

       3、构造函数对二叉树的特殊处理:将二叉树中每个结点的空指针引出一个虚结点,其值为一特定值,如‘#’,以标识其为空。

               4、二叉链表属于动态内存分配,需要在析构函数中释放二叉链表的所有结点。在释放某结点时,该结点的左右都子树已经释放,所以应该采用后序遍历。

    3、主函数

     1 #include"bitree.h"
     2 using namespace std;
     3 
     4 int main()
     5 {
     6     BiTree<char>* bitree=new BiTree<char>();        // 创建一棵二叉树
     7     bitree->PreOrder();                             // 前序遍历
     8     cout << endl;
     9     bitree->InOrder();                              // 中序遍历
    10     cout << endl;
    11     bitree->PostOrder();                            // 后序遍历
    12     cout << endl;
    13     bitree->LeverOrder();                           // 层序遍历
    14     delete bitree;
    15 
    16     system("pause");
    17     return 0;
    18 }

    三、实例

      建立如下二叉树,并输出四种遍历的结果。

    运行结果:

     结果正确。

    参考文献:

    [1]王红梅, 胡明, 王涛. 数据结构(C++版)[M]. 北京:清华大学出版社。

    马上元旦了,祝大家元旦快乐!!2017-12-29

  • 相关阅读:
    poj 2763 Housewife Wind
    hdu 3966 Aragorn's Story
    poj 1655 Balancing Act 求树的重心
    有上下界的网络流问题
    URAL 1277 Cops and Thieves 最小割 无向图点带权点连通度
    ZOJ 2532 Internship 网络流求关键边
    ZOJ 2760 How Many Shortest Path 最大流+floyd求最短路
    SGU 438 The Glorious Karlutka River =) 拆点+动态流+最大流
    怎么样仿写已知网址的网页?
    5-10 公路村村通 (30分)
  • 原文地址:https://www.cnblogs.com/smile233/p/8145760.html
Copyright © 2011-2022 走看看