数据结构之二叉树————C++实现

这篇博客重点介绍数据结构的C++实现,不太设计理论细节,如果想了解细节,可以去参照严蔚敏老师的数据结构一书。

一般的树都可以转换为二叉树,且二叉树的存储结构及操作都较为简单,因此先介绍下树中的二叉树类型。

二叉树是n个结点的有限集,它或者是空集(n=0),或者有一个根结点及最多两棵互不相交的,分别称作这个根的左子树和右子树的二叉树组成。

二叉树有五种基本形态:(1)空集(2)根的左右子树都为空(3)根的右子树为空(4)根的左子树为空(5)根的左右子树皆不为空。

二叉树的逻辑结构:

二叉树与无序树不同,二叉树中,每个结点最多只能有两棵子树,并且无左右之分。另外,二叉树与度数为2的有序树不同,在有序树中,虽然一个极点的孩子之间是有左右次序的,但若该结点只有一个孩子,就无须区分其左右次序;而二叉树中,即使是一个孩子也有左右之分。所以,二叉树不是树的特殊情形。

二叉树的特殊性质(在此不证明):

(1)二叉树第i层上的结点数目最多为2i-1(i>=1);

 (2)深度为k的二叉树至多有2k-1个结点(k>=1);

 (3)在任意一颗二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1

 完全二叉树:若二叉树的高度为h,除第h层外,其他各层(1-h-1)的结点数都达到最大个数,并且最下一层上的结点都集中在该层最左边的若干位置上,则此二叉树被称为完全二叉树。

 满二叉树:一颗深度为k且有2k-1个结点的二叉树称为满二叉树。

 还有很多性质,在这不在多讨论。可以参考其他书籍。

下面介绍下二叉树的实现:

顺序存储结构实现的主要思想:将一般二叉树填上一些空结点,使之成为“完全二叉树”,并且按完全二叉树形式给结点编号。其中的空结点便是顺序存储过程中浪费的空间。

 

///////////////////////////////////////////////////////////////////
///SeqBinaryTree.h 二叉树(采用顺序存储结构)数据结构C++类定义(基类)
///////////////////////////////////////////////////////////////////

#ifndef SEQBINARYTREE_H
#define SEQBINARYTREE_H
#include<iostream>
#include "SeqBinaryTree.h"
using namespace std;
template<typename ElemType>
class SeqBinaryTree
{
public:
    // 把一棵顺序存储的二叉树置空
    void clear();
    // 取最后一个节点的顺序存储空间的下标
    int getFinalIndex();
    //返回二叉树的首地址
    ElemType* getInitialAddress();
    //取下标为i的结点(即第i+1个结点)
    ElemType getNode(int i);
    //设置下标为i的结点(即第i+1个结点)的值
    void setNode(int i, ElemType value);
    //设置最后一个结点的下标
    void setFinalIndex(int i);
    //重载赋值运算符的定义
    SeqBinaryTree<ElemType> operater = (SeqBinaryTree<ElemType> rightT);
    void read(istream& in);
    void display(ostream& out);
    //******下面为系统自动调用的构造函数和析构函数声明********
    
    //构造函数
    SeqBinaryTree();
    //析构函数
    ~SeqBinaryTree();
    //拷贝初始化构造函数
    SeqBinaryTree(const SeqBinaryTree<ElemType>& seqT);
protected:
    ElemType* initialAddress;
    int finalIndex;
};

///二叉树的C++类实现

//功能:把一棵顺序存储的树置空
template<typename ElemType>
void SeqBinaryTree<ElemType>::clear()
{
    if (initialAddress)
    {
        delete[] initialAddress;
        initialAddress = NULL;
        finalIndex = -1;
    }
}
//取最后一个结点在顺序存储空间的下标
template <typename ElemType>
int SeqBinaryTreee<ElemType>::getFinalIndex()
{
    return finalIndex;
}
//返回二叉树的首地址
template <typename ElemType>
ElemType* SeqBinaryTree<ElemType>::getInitialAddress()
{
    retuen initialAddress;
}
//取下标为i的结点(即第i+1个点)
template <typename ElemType>
ElemType SeqBinaryTree<ElemType>::getNode(int i)
{
    if (i < 0 || i>finalIndex)
    {
        cerr << OVERFLOW;
        exit(1);
    }
    return initialAddress[i];
}
//重载赋值运算符的定义
template<typename ElemType>
SeqBinaryTree<ElemType> SeqBinaryTree<ElemType>::operater = (SeqBinaryTree<ElemType> rightT)
{
    if (this != &rightT)
    {
        finalIndex = rightT.finalIndex;

        if (finalIndex != -1)
        {
            initialAddress = new ElemType[finalIndex + 1];
            assert(initialAddress != 0);

            for (int i = 0; i <= finalIndex; i++)
            {
                initialAddress[i] = rightT.initialAddress[i];
            }
        }
    }

    return *this;
}

//设置下标为i的结点(即第i+1个结点)的值
template<typename ElemType>
void SeqBinaryTree<ElemType>::setNode(int i, ElemType value)
{
    initialAddress[i] = value;
    //如果i大于最后一个结点在顺序存储空间的下标
    if (i>finalIndex)
    {
        finalIndex = i;
    }
}
//设置最后一个结点在顺序存储空间的下标
template <typename ElemType>
void SeqBinaryTree<ElemType>::setFinalIndex(int i)
{
    finalIndex = i;
}
//***********************构造函数,析构函数,拷贝初始化函数*************

//构造函数
template<typename ElemType>
SeqBinaryTree<ElemType>::SeqBinaryTree()
{
    initialAddress = NULL;
    finalIndex = -1;
}
//析构函数
template<typename ElemType>
SeqBinaryTree<ElemType>::~SeqBinaryTree()
{
    clear();
}

//拷贝初始化函数
template<typename ElemType>
SeqBinaryTree<ElemType>::SeqBinaryTree(const ElemType& seqT)
{
    initialAddress = NULL;//设置当前树的顺序存储空间的首地址为空
    finalIndex = seqT.finalIndex;
    if (finaIndex != -1)
    {
        initialAddress = new ElemType[finalIndex + 1];
        assert(initialAddress != 0);

        for (int i = 0; i < = finalIndex; i++)
        {
            initialAddress[i] = seqT.initialAddress[i];
        }
    }
}
//输入顺序存储二叉树
template<typename ElemType>
void SeqBinaryTree<ElemType>::read(istream& in)
{    
    int i = finalIndex;
    while (in>> initialAddress.[i])
    {
        i++;
    }
    finalIndex = i;
}
//重载输入运算符
template<typename ElemType>
istream& operator >> (istream& in, SeqBinaryTree<ElemType>& T)
{
    T.read(in);
    return in;
}
//输出顺序存储二叉树
template<typename ElemType>
void SeqBinaryTree<ElemType>::display(ostream& out)
{
    int i = 0;
    while (i!=finalIndex)
    {
        out << initialAddress[i];
        i++;
    }
}
//重载输出运算符
template<typename ElemType>
ostream& operator << (ostream& out, SeqBinaryTree<ElemType>& T)
{
    T.diplay(out);
    return out;
}
#endif

 

 

 

 如果二叉树不是满二叉树、完全二叉树,或是二叉树经常执行插入和删除等操作时,都应考虑用链式存储结构存放二叉树。

二叉树链式存储结构的实现:

用这种方式存储二叉树时,每个结点除了存储结点本身的数据data外,还应存储一个左指针lchild和一个右指针rchild,分别指向左孩子和右孩子。如果在操作中需要查找结点的双亲,可以再加上指向双亲结点的指针parent。

在这给出的结构中不包括双亲结点。

#include <iostream>
#include<assert.h>
#include "SqQueue.h"//用到顺序存储结构实现的队列(前一篇博客已经给出)
#include "SeqBinaryTree.h"//用到顺序存储结构实现二叉树(上边已经给出)
#include "SqStack.h"//用到顺序存储结构实现的栈(以后将会给出)
using namespace std;
#define LH 1 //左高
#define EH 0 //等高
#define RH -1 //右高

/////////////////////////////////////////////////////////
//二叉树(采用二叉链表存储)结点的数据结构C++类声明
template<typename ElemType>
class LinkBinaryTree
{
public:
    //二叉树(采用二叉链表存储)结点的数据结构C++定义
    class Node
    {
    public:
        Node() :lchild(NULL), rchild(NULL){};//Node的构造函数,左右指针初始化为空

        ElemType data;  //结点的数据域
        class Node* lchild;//结点的左指针
        class Node* rchild;//结点的右指针
    };
    typedef Node* NodePointer;

public:
    //把二叉树置空
    void clear();
    //求二叉树的叶子数
    int countLeaf();
    //求二叉树的结点数
    int countNode();
    //递归求二叉树的深度
    int depth();
    //显示二叉树的顺序存储结构
    void displaySeqTree();
    //交换二叉树中所有结点的左右子树
    void exchangeRchild();
    //取根指针
    NodePointer getRoot();
    //中序递归遍历二叉树
    void  inOrderTraverse();
    //判断是否为空二叉树
    bool isEmpty();
    //按层次顺序遍历二叉树
    void layOrderTraverse();
    //二叉树的二叉链表存储转换为顺序存储结构
    void linkToSequential();
    //非递归中序遍历二叉树
    void noRecursionInOrderTraverse();
    //后序递归遍历二叉树
    void postOrderTraverse();
    //前序递归遍历二叉树
    void preOrderTraverse();
    //随机生成一颗二叉树
    void randomCreate();
    //二叉树的顺序存储转换为二叉链表存储结构
    void sequentialToLink(SeqBinariTree<ElemType> T);
    
//为实现公有操作定义的辅助函数
private:
    //拷贝初始化构造函数的辅助函数
    void LinkBinaryTree_aux(NodePointer& p, NodePointer other);
    //求二叉树叶子数的辅助函数
    int countLeaf_aux(NodePointer p);
    //求二叉树结点数的辅助函数
    int countNode_aux(NodePointer p);
    //回收二叉树结点存储空间的辅助函数
    void deleteNode_aux(NodePointer p);
    //递归求二叉树深度的辅助函数
    int depth_aux(NodePointer p);
    //交换二叉树中所有结点左右子树的辅助函数
    void exchangeLRchild_aux(NodePointer p);
    //中序递归遍历二叉树的辅助函数
    void inOrderTraverse_aux(NodePointer p);
    //二叉树的二叉链表存储转换为顺序存储结构的辅助函数
    void linkToSequential_aux(SeqBinaryTree<ElemType>& tempT, NodePointer p, int i);
    //后续递归遍历二叉树的辅助函数
    void postOrderTraverse_aux(NodePointer p);
    //前序递归遍历二叉树的辅助函数
    void preOrderTraverse_aux(NodePointer p);
    //二叉树的顺序存储结构转换为二叉链表存储结构的辅助函数
    void sequentialToLink_aux(int i, NodePointer& sybroot);

/////////////////////////下面为系统自动调用的构造函数、析构函数及输入输出函数的声明

public:
    //二叉树(采用二叉链表存储)构造函数
    LinkBinaryTree();
    //二叉树(采用二叉链表存储)析构函数
    virtual ~LinkBinaryTree();
    //二叉树(采用二叉链表存储)拷贝初始化构造函数
    LinkBinaryTree(const LinkBinaryTree<ElemType>& otherT);
    //输入二叉树(采用二叉链表存储)
    void read(istream& in);
    //输出二叉树(采用二叉链表存储)
    void display(ostream& out);
protected:
    NodePointer root;//二叉树的根指针(采用二叉链表存储)
    SeqBinaryTree<ElemType> seqT;//二叉树对应的顺序存储树
};


 ///////二叉树(采用二叉链表存储)数据结构C++实现   

//功能:把二叉树置空
template <typename ElemType>
void LinkBinaryTree<ElemType>::clear()
{
    seqT.clear();
    deleteNode_aux(root);
    root = NULL;
}


//回收二叉树结点存储空间的辅助函数
template<typename ElemType>
void LinkBinaryTree<ElemType>::deleteNode_aux(NodePointer p)
{    
    //按后续遍历方式逐一回收没一个结点
    if (p)
    {
        deleteNode_aux(p->lchild);
        deleteNode_aux(p->rchild);
        delete p;
    }
}
//求二叉树的叶子数
template<typename ElemType>
int LinkBinaryTree<ElemType>::countLeaf()
{
    return countLeaf_aux(root);
}

//求二叉树叶子数的辅助函数
template <typename ElemType>
int LinkBinaryTree<ElemType>::countLeaf_aux(NodePointer p)
{
    int num;//预存放最终的叶子结点数
    static int i = 0;//存放叶子结点累计数,初始化为0
    if (p)
    {   //如果指针p不为空且所指结点的左右子树指针均为空,则叶子结点累计数加1.
        if (!p->lchild && !p->rchild)
        {
            ++i;
        }
        countLeaf_aux(p->lchild);
        countLeaf_aux(p->rchild);
    }
    //如果此时递归过程指针p指向了根节点,说明已经累计完所有叶子几点数。(涉及到递归的知识。)
    if (p == root)
    {
        num = i;//记录叶子结点数
        i = 0;//叶子结点累计数清0,以便再次调用此函数求叶子结点数。
    }
    return num;
}

//求二叉树的结点数
template <typename ElemType>
int LinkBinaryTree<ElemType>::countNode()
{
    return countNode_aux(root);
}

//求二叉树结点数的辅助函数
template<typename ElemType>
int LinkBinaryTree<ElemType>::countNode_aux(NodePointer p)
{
    int num;//预存放最终结点个数
    static int i = 0;//存放结点累计数,初始化为0
    if (p)//如果p不为空
    {
        i++;
        countNode_aux(p->lchild);
        countNode_aux(p->rchild);
    }
    //同上
    if (p==root)
    {
        num = i;
        i = 0;
    }
    return num;
}

//递归求二叉树的深度
template<typename ElemType>
int LinkBinaryTree<ElemType>::depth()
{
    return depth_aux(root);
}

//递归求二叉树深度的辅助函数
template <typename ElemType>
int LinkBinaryTree<ElemType>::depth_aux(NodePointer p)
{
     int lDep =0;//预存放左子树的深度
     int rDep=0;//预存放右子树的深度
    if (!p)
    {
        return 0;
    }
    else
    {
        lDep = depth_aux(p->lchild);
        rDep = depth_aux(p->rchild);
        return (lDep > rDep ? lDep : rDep) + 1;
    }

}
//交换二叉树中所有结点的左右子树
template <typename ElemType>
void LinkBinaryTree<ElemType>::exchangeRchild()
{
    exchangeLRchild_aux(root);
    //重新生成二叉树对应的顺序存储结构
    linkToSequential();
}

//交换二叉树中所有结点左右子树的辅助函数
template <typename ElemType>
void LinkBianryTree<ElemType>::exchangeLRchild_aux(NodePointer p)
{
    NodePointer s;//预存放当前结点左孩子的指针
    if (p)
    {
        exchangeLRchild_aux(p->lchild);
        exchangeLRchild_aux(p->rchild);
        s = p->lchild;
        p->lchild = p->rchild;
        p->rchild = s;
    }
}

//二叉树的二叉链表存储转换为顺序存储结构

template <typename ElemType>
void LinkBinaryTree<ElemType>::linkToSequential()
{
    int max_total;//预存放具有同样深度的满二叉树的结点数
    SeqBinaryTree<ElemType> tempT;//临时顺序存储结构的二叉树tempT
    if (!root)
    {
        seqT.clear();
        return;
    }
    //计算具有相同深度的满二叉树的结点数
    max_total = 1;
    for (int d = 1; d <= depth(); ++d)
    {
        max_total *= 2;
    }
    max_total - = 1;
    //根据对应的满二叉树的结点数,申请max_total个结点
    tempT.initialAddress = new Node[max_total];
    tempT.finalIndex = max_total - 1;
    linkToSequential_aux(tempT, root, 0);
    seqT = tempT;
}

//二叉树的二叉链表存储 转换为顺序存储结构的辅助函数
//各节点对应的顺序存储结构位置为0
template <typename ElemType>
void LinkBinaryTree<ElemType>::linkToSequential_aux(SeqBinaryTree<ElemType>& tempT, NodePointer p, int i)
{
    //用指针p所指结点的数据域设置为二叉树顺序存储空间下标为i的值
    tempT.setNode(i, p->data);
    //用指针p所指结点的左指针和2*i+1下标值自递归
    //把左子树的二叉链表存储转换为顺序存储结构
    if (p->lchild != NULL)
    {
        linkToSequential_aux(tempT, p->lchild, 2 * i + 1);
    }
    //用指针p所指结点的左指针和2*i+2下标值自递归
    //把右子树的二叉链表存储转换为顺序存储结构
    if (p->rchild != NULL)
    {
        linkToSequential_aux(tempT, p->rchild, 2 * i + 2);
    }
}
//取根指针
template<typename ElemType>
LinkBinaryTree<ElemType>::NodePointer LinkBinaryTree<ElemType>::getRoot()
{
    return root;
}
//中序递归遍历二叉树
template<typename ElemType>
void LinkBinaryTree<ElemType>::inOrderTraverse()
{
    inOrderTraverse_aux(root);
}
//中序递归遍历二叉树的辅助函数
template <typename ElemType>
void LinkBinaryTree<ElemType>::inOrderTraverse_aux(NodePointer p)
{
    if (p)
    {
        inOrderTraverse_aux(p->lchild);
        cout << p->data;
        inOrderTraverse_aux(p->rchild);
    }
}

//判断是否为空二叉树
template <typename ElemType>
bool LinkBinaryTree<ElemType>::isEmpty()
{
    return root ? false : true;
}
//按层次顺序遍历二叉树
template<typename ElemType>
void LinkBinaryTree<ElemType>::layOrderTraverse()
{
    NodePointer p;//预指向当前遍历节点的指针
    SeQueue<NodePointer>Q;//预存放待遍历结点的指针队列
    if (root!=NULL)
    {
        Q.enQueue(p);
    }
    while (!Q.isEmpty())
    {
        Q.deQueuue(p);
        cout << p->data;
        if (p->lchild)
        {
            Q.enQueue(p->lchild);
        }
        if (p->rchild)
        {
            Q.enQueue(p->rchild);
        }
    }
}


//非递归中序遍历二叉树
template <typename ElemType>
void LinkBinaryTree<ElemType>::noRecursionInOrderTraverse()
{
    NodePointer p = root;//指向当前结点的指针,初始化为根指针
    SqStack<ElemType> S;
    while (p||!S.isEmpty())
    {
        if (p)
        {
            S.push(p);
            p = p->lchild;
        }
        else
        {
            S.pop(p);
            cout << p->data;
            p = p->rchild;
        }

    }
}

//后序递归遍历二叉树
template <typename ElemType>
void LinkBinaryTree<ElemType>::postOrderTraverse()
{
    postOrderTraverse_aux(root);
}
//后序遍历二叉树的辅助函数
template<typename ElemType>
void LinkBinaryTree<ElemType>::postOrderTraverse_aux(NodePointer p)
{
    if (p)
    {
        postOrderTraverse_aux(p->lchild);
        postOrderTraverse_aux(p->rchild);
        cout << p->data;
    }
}

//前序遍历二叉树
template <typename ElemType>
void LinkBinaryTree<ElemType>::preOrderTraverse()
{
    preOrderTraverse_aux(root);
}

//前序遍历二叉树的辅助函数
template <typename ElemType>
void LinkBinaryTree<ElemType>::preOrderTraverse_aux(NodePointer p)
{
    if (p)
    {
        cout << p->data;
        preOrderTraverse_aux(p->lchild);
        preOrderTraverse_aux(p->rchild);
    }
}

//二叉树的顺序存储转换为二叉链表存储结构
template <typename ElemType>
void LinkBinaryTree<ElemType>::sequentialToLink(SeqBinaryTree<ElemType> T)
{
    seqT = T;
    sequentialToLink_aux(0, root);
}

//二叉树的顺序存储转换为二叉链表存储结构的辅助函数
template <typename ElemType>
void LinkBinaryTree<ElemtType>::sequentialToLink(int i, NodePointer p)
{
    int n = seqT.getFinalIndex();//得到顺序存储二叉树最后一个结点的下标

    if (n==-1)//如果顺序存储二叉树最后一个结点的下标为-1,则指针p设置空
    {
        p = NULL;
        return;
    }
    //如果顺序存储二叉树最后一个结点的下标不为-1,则申请一个二叉链表
    p = new LinkBinaryTree<ElemType>::Node;
    assert(p != 0);

    //把顺序存储二叉树下标为i的结点的数据域赋值给新结点的数据域
    p->data = seqT.getNode(i);
   //如果顺序存储二叉树下标为i的结点无左子树,则新结点的左指针为空,否则,
    //用下标为2*i+1和新结点的左指针自递归,把下标为i的结点的左子树转换为
    //二叉链表存储结构
    if (2 * i + 1>n || seqT.getNode(2 * i + 1) == ' ')
    {
        p->lchild = NULL;
    }
    else
    {
        sequetialToLink_aux(2 * i + 1, p->lchild);
    }
    //如果顺序存储二叉树下标为i的结点无右子树,则新结点的左指针为空,否则,
    //用下标为2*i+2和新结点的左指针自递归,把下标为i的结点的右子树转换为
    //二叉链表存储结构
    if (2*i+2>n||seqT.getNode(2*i+2)==' ')
    {
        p->rchild = NULL;
    }
    else
    {
        sequentialToLink_aux(2 * i + 2, p->rchild);
    }
}

//************下面为系统自动调用的构造函数、析构函数及输入输出函数的实现***********
//二叉树(采用二叉链表存储)构造函数
template<typename ElemType>
LinkBinaryTree<ElemType>::LinkBinaryTree()
{
    root = NULL;
    seqT.clear();
}

//二叉树拷贝初始化函数
template<typename ElemType>
LinkBinaryTree<ElemType>::LinkBinaryTree(const LinkBinaryTree<ElemType>& otherT)
{
    if (!otherT.root)
    {
        root = NULL;
        seqT.clear();
    }
    else
    {
        LinkBinaryTree_aux(root, otherT.root);
        linkToSequential();
    }
}

//拷贝初始化构造函数的辅助函数
template<typename ElemType>
void LinkBinaryTree<ElemType>::LinkBinaryTree_aux(NodePointer& p, NodePointer otherP)
{
    if (!otherP)
    {
        p = NULL;
        return;
    }
    p = new Node;
    assert(p != 0);
    p->data = otherP->data;
    if (!otherP->lchild)
    {
        p->lchild = NULL;
    }
    else
    {
        LinkBinaryTree_aux(p->lchild, otherP->lchild);
    }
    if (!otherP->rchild)
    {
        p->rchild = NULL;
    }
    else
    {
        LinkBinaryTree_aux(p->rchild, otherP->rchild);
    }
}

//二叉树析构函数
template<typename ElemType>
LinkBinaryTree<ElemType>::~LinkBinaryTree()
{
    clear();
}

//输入二叉树(采用二叉链表存储)
template<typename ElemType>
void LinkBinaryTree<ElemType>::read(istream& in)
{
    cout << "存储方式创建一颗二叉树" << endl;
    seqT.read();
    sequentialToLink_aux(0, root);
}

//重载输入运算符的定义
template<typename ElemType>
istream& operator>>(istream& in, LinkBinaryTree<ElemType>& bT)
{
    bT.read();
    return in;
}

//输出二叉树
template<typename ElemType>
void LinkBinaryTree<ElemType>::display(ostream& out)
{
    out << seqT.initialAddress;
}

//重载输出运算符

template<typename ElemType>
ostream& operator<<(ostream& out, LinkBinaryTree<ElemType>& bT)
{
    bT.display(out);
    return out;
}

水平有限,难免有错误之处,请指正。

 

posted on 2014-04-14 21:12  IT先生  阅读(1190)  评论(0编辑  收藏  举报