数据结构之二叉树————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; }
水平有限,难免有错误之处,请指正。