資料結構(C++) 二叉樹模板類
阿新 • • 發佈:2018-12-30
1.二叉樹
遞迴定義:一顆二叉樹是結點的有限的集合,該集合或者為空或者是由一個根節點加上兩顆分別稱為左子樹和右子樹、互不相交的二叉樹的組合。
二叉樹的左右子樹都還是二叉樹,到達空子樹的時候遞迴定義結束。許多基於二叉樹的演算法都利用了這個遞迴特性。
二叉樹的特點是每個節點最多有兩個子女,分別稱為左右子女。在二叉樹中不存在度數大於2的節點,並且二叉樹的子樹有左右之分,其子樹的次序不能顛倒。
2.BinaryTree.h檔案
#ifndef BINARYTREE_H
#define BINARYTREE_H
#include<stack>
#include< iostream>
#include<stdlib.h>
#include<istream>
#include<queue>
#include<stack>
using namespace std;
//節點結構體
template <class T>
struct BinTreeNode
{
T data ;
BinTreeNode<T> *leftChild;
BinTreeNode<T> *rightChild;
BinTreeNode():leftChild(NULL),rightChild(NULL ) {}
BinTreeNode(T x,BinTreeNode<T> *l=NULL,BinTreeNode<T> *r=NULL):leftChild(l),rightChild(r),data(x) {}
};
//後序遍歷的非遞迴演算法用到的節點結構體
template <class T>
struct stacknode
{
BinTreeNode<T> *ptr;
int tag ;
stacknode(BinTreeNode<T> *n=NULL):ptr(n),tag(1) {}
};
//二叉樹類
template <class T>
class BinaryTree
{
public:
//根指標
BinTreeNode<T> *root;
/** Default constructor */
BinaryTree():root(NULL) {}
//拷貝建構函式
BinaryTree(const BinaryTree<T> &s)
{
root=Copy(s.root);
}
~BinaryTree()
{
destroy(root);
}
//判斷二叉樹是否為空
bool IsEmpty()
{
return (root!=NULL)?true:false;
}
//返回父節點
BinTreeNode<T> *Parent(BinTreeNode<T> *current)
{
return (root==NULL||root==current)?NULL:Parent(root,current);
}
//返回左節點
BinTreeNode<T> *LeftChild(BinTreeNode<T> *current)
{
return (current==NULL)?NULL:current->leftChild;
}
//返回右節點
BinTreeNode<T> *RightChild(BinTreeNode<T>*current)
{
return(current==NULL)?NULL:current->rightChild;
}
//返回樹的高度
int Height()
{
return Height(root);
}
//返回節點數
int Size()
{
return Size(root);
}
//獲得根節點
BinTreeNode<T> * getRoot ()
{
return root;
}
////遞迴演算法遍歷二叉樹
// //前序遍歷
// void preOrder(void(*visit)(BinTreeNode<T> *p))
// {
// //遞迴演算法
// preOrder(root,visit);
// }
// //中序遍歷
// void inOrder(void(*visit)(BinTreeNode<T> *p))
// {
// inOrder(root,visit);
// }
// //後序遍歷
// void postOrder(void(*visit)(BinTreeNode<T> *p))
// {
// postOrder(root,visit);
// }
//每一次訪問一個節點後,在向左子樹遍歷下去之前,
//利用這個棧記錄該節點的右子女(如果有的話)節點地址,
//以便左子樹退回時可以直接從棧中取出右子樹的根節點,繼續其右子樹的前序遍歷
void preOrder(void(*visit)(BinTreeNode<T> *p))
{
stack<BinTreeNode<T> *> nodes;
BinTreeNode<T> *temp=root;
nodes.push(NULL);
while(temp!=NULL)
{
visit(temp);
if(temp->rightChild!=NULL)
{
nodes.push(temp->rightChild);
}
if(temp->leftChild!=NULL)
{
temp=temp->leftChild;
}
else
{
temp=nodes.top();
nodes.pop();
}
}
}
//中序遍歷
//在一棵子樹中首先訪問的是中序下的第一個節點
//它位於從根開始的沿著leftchild鏈走到最左下角的節點,該節點的leftchild為NULL。
//訪問完他的資料之後,再遍歷它的右子樹。如果此右子樹又是二叉樹,則重複上面的過程,直到該子樹遍歷完畢。
void inOrder(void(*visit)(BinTreeNode<T> *p))
{
stack<BinTreeNode<T> *> nodes;
//遍歷指標,從根節點開始
BinTreeNode<T> *temp=root;
do
{
//遍歷指標未到最左下的節點,不空
while(NULL!=temp)
{
//該子樹沿途節點進棧
nodes.push(temp);
temp=temp->leftChild;
}
if(!nodes.empty())
{
//棧不為空的時候退棧,訪問根節點,遍歷指標進入右子女節點
temp=nodes.top();
visit(temp);
nodes.pop();
temp=temp->rightChild;
}
}
while(NULL!=temp||!nodes.empty());
}
//後序遍歷
//後序遍歷比先前的兩種遍歷要複雜得多,在遍歷完左子樹之後還不能訪問根節點,
//需要再遍歷右子樹,等到右子樹遍歷完畢之後才能訪問根節點。所以在棧工作記錄中一定先表明剛才是在左子樹<1>還是在右子樹<2>中。
//首先使用棧暫存根節點的地址,再向左子樹遍歷下去,此時根節點的tag為1,當訪問完根節點的左子樹之後從左子樹退回,還要去遍歷右子樹
//此時改跟的tag為2.
//從右子樹中退出時才能訪問位於棧頂的根節點的值。
void postOrder(void(*visit)(BinTreeNode<T> *p))
{
stack<stacknode<T> > nodes;
stacknode<T> temp;
BinTreeNode<T> *p=root;
do
{
while(NULL!=p)
{
temp.ptr=p;
temp.tag=1;
nodes.push(temp);
p=p->leftChild;
}
int continue1=1;
while(continue1&&!nodes.empty())
{
temp=nodes.top();
nodes.pop();
p=temp.ptr;
switch(temp.tag)
{
case 1:
temp.tag=2;
nodes.push(temp);
continue1=0;
p=p->rightChild;
break;
case 2:
visit(p);
break;
}
}
}
while(!nodes.empty());
}
//層次遍歷
//層次遍歷從二叉樹的根節點開始,自上向下,自左向右,分層依次訪問樹中的各個節點、
void levelOrder(void (*visit)(BinTreeNode<T> *p))
{
queue<BinTreeNode<T> *> nodes;
BinTreeNode<T> *temp=root;
BinTreeNode<T> *tnode;
nodes.push(temp);
while(!nodes.empty())
{
tnode=nodes.front();
nodes.pop();
visit(tnode);
if(NULL!=tnode->leftChild)
{
nodes.push(tnode->leftChild);
}
if(NULL!=tnode->rightChild)
{
nodes.push(tnode->rightChild);
}
}
}
//插入新元素
void Insert(const T &item) {}
//搜尋
BinTreeNode<T> *Find(T &item) const {}
void CreateBinTree()
{
CreateBinTree(root);
}
//前序遍歷輸出
void Traverse(BinTreeNode<T> * subTree)
{
if(NULL!=subTree)
{
cout<<subTree->data<<" ";
//遞迴輸出subTree的左子樹
Traverse(subTree->leftChild);
//遞迴輸出subTree的右子樹
Traverse(subTree->rightChild);
}
}
protected:
//利用二叉樹前序遍歷建立二叉樹
void CreateBinTree(BinTreeNode<T> *&subTree)
{
T item;
if((cin>>item)&&item!="#")
{
subTree=new BinTreeNode<T>(item);
if(subTree==NULL)
{
cerr<<"儲存分配錯誤!"<<endl;
exit(1);
}
CreateBinTree(subTree->leftChild);
CreateBinTree(subTree->rightChild);
}
else
{
//封閉指向空子樹的指標
subTree=NULL;
}
}
//插入
bool Insert(BinTreeNode<T> *&subTree,const T& x) {}
//搜尋
bool Find(BinTreeNode<T> *subTree,const T& x)const {}
//根據值查詢結點
BinTreeNode<T>* Find(BinTreeNode<T> *subTree,const T &x ) {}
//拷貝
BinTreeNode<T> *Copy(BinTreeNode<T> *originnode)
{
if(originnode==NULL)
{
return NULL;
}
BinTreeNode<T> *temp=new BinTreeNode<T>();
temp->data=originnode->data;
temp->leftChild=originnode->leftChild;
temp->rightChild=originnode->rightChild;
return temp;
}
//解構函式
void destroy(BinTreeNode<T> *subTree)
{
if(subTree!=NULL)
{
destroy(subTree->leftChild);
destroy(subTree->rightChild);
delete subTree;
}
}
//返回父節點,從節點subTree開始,搜尋current節點的父節點
BinTreeNode<T> *Parent(BinTreeNode<T> *subTree,BinTreeNode<T> *current)
{
if(current==NULL)
return NULL;
//找到返回父節點
if(subTree->leftChild==current||subTree->rightChild==current)
return subTree;
BinTreeNode<T> *p;
//在左子樹種遞迴查詢
if((p=Parent(subTree->leftTree,current))!=NULL)
return p;
//在右子樹種遞迴查詢
else
return Parent(subTree->rightChild,current);
}
//返回樹的高度
int Height(BinTreeNode<T> *subTree)
{
if(subTree==NULL)
{
return 0;
}
else
{
int leftHieght=Height(subTree->leftChild);
int rightHeight=Height(subTree->rightChild);
return (leftHieght>rightHeight)?leftHieght+1:rightHeight+1;
}
}
//返回根節點
int Size(BinTreeNode<T> *subTree) const
{
if(subTree==NULL)
{
//遞迴結束,節點個數為0
return 0;
}
else
{
return 1+Size(subTree->leftChild)+Size(subTree->rightChild);
}
}
//前序遍歷
void preOrder(BinTreeNode<T>*subTree,void(*visit)(BinTreeNode<T> *p))
{
if(subTree!=NULL)
{
visit(subTree);
preOrder(subTree->leftChild,visit);
preOrder(subTree->rightChild,visit);
}
}
//中序遍歷
void inOrder(BinTreeNode<T> *subTree,void(*visit)(BinTreeNode<T> *p))
{
//遞迴函式,=NULL是終止遞迴條件
if(subTree!=NULL)
{
inOrder(subTree->leftChild,visit);
visit(subTree);//訪問根節點
inOrder(subTree->rightChild,visit);
}
}
//後序遍歷
void postOrder(BinTreeNode<T> *subTree,void(*visit)(BinTreeNode<T> *p))
{
if(subTree!=NULL)
{
postOrder(subTree->leftChild,visit);
postOrder(subTree->rightChild,visit);
visit(subTree);
}
}
};
#endif // BINARYTREE_H
3.main函式
#include <iostream>
#include<BinaryTree.h>
#include<string>
template <class T>
void visit(BinTreeNode<T> *p );
using namespace std;
int main()
{
BinaryTree<string> *bin=new BinaryTree<string>();
bin->CreateBinTree();
if(bin->IsEmpty())
{
cout<<"當前樹不為空,輸出所有節點:"<<endl;
bin->Traverse(bin->root);
cout<<endl;
cout<<"當前樹的高度是:"<<bin->Height()<<endl;
cout<<"當前樹的大小是:"<<bin->Size()<<endl;
cout<<"前序遍歷:"<<endl;
bin->preOrder(visit);
cout<<"\n中序遍歷:"<<endl;
bin->inOrder(visit);
cout<<"\n後序遍歷:"<<endl;
bin->postOrder(visit);
cout<<endl;
cout<<"樹的根節點是:"<<bin->getRoot()->data;
}
delete bin;
return 0;
}
template < class T>
void visit(BinTreeNode<T> *p )
{
cout<< p->data<<" ";
}
下面是一顆二叉樹:
模板類儲存執行結果:
完整的工程程式碼,請點選這裡