在描述二叉树,堆,优先队列之前之前,有必要介绍节点和向量,在二叉树,队,优先队列中会有使用:
二叉树可以用链接在一起的一个个节点描述,每个节点包含一个数据元素和指向左右子树的指针,可以用类定义节点(结构体也可以):
template<typename T>
class treenode{
public:
T ele;
treenode*left;
treenode*right;
treenode(){ele=0;left=NULL;right=NULL;}
treenode(T el){ele=el;left=NULL;right=NULL;}
};
堆和优先队列虽然都是二叉树结构,也可以由二叉树表示,但是不及数组或者向量表示,尤其是面对数目未定增删多的时候,向量首选:
template<typename T>
class vecs{
private:
T *ele;
int size;
int cap;
public:
vecs<T>(){size=0;cap=16;ele=new T(cap);}
void sure(){if(size>cap)
{T *old=ele;
cap=2*size;
ele=new T(cap);
for(int i=0;i<size;i++)
ele[i]=old[i];
delete old;}}
void push_back(T ele1){
sure();
ele[size++]=ele1;}
void pop_back(){
if(size==0) throw runtime_error("empty");
--size;}
void clear(){
while(size!=0){
pop_back();}}
int getsize(){return size;}
bool empty(){return (size==0);}
T at(int index){
if(index<0||index>cap)throw runtime_error("empty");
return ele[index];}
T&operator[](int index){
if(index<0||index>cap)throw runtime_error("empty");
return ele[index];}
void swap(vecs&v1){
int len=v1.getsize();
if(len>=size){
T *p=new T[len];
for(int i=0;i<size;i++)
{p[i]=ele[i];}
clear();
for(int i=0;i<len;i++)
push_back(v1.at(i));
v1.clear();
for(int i=0;i<len;i++)
{v1.push_back(p[i]);}}
else{
T *p=new T[size];
for(int i=0;i<size;i++)
{p[i]=ele[i];}
clear();
for(int i=0;i<len;i++)
{ele[i]=v1.at(i);}
v1.clear();
for(int i=0;i<size;i++)
{v1.push_back(p[i]);}
}
}
};
常用的二叉树是二叉搜索树,即:任意节点大于其左节点小于其右节点:涉及到插入,前序,后序,中序操作:
template<typename T>
class bitree{
private:treenode<T>*root;
int size;
void inorder(treenode<T>*root);
void preorder(treenode<T>*root);
void postorder(treenode<T>*root);
public:
bitree(){root=NULL;size=0;}
bitree(T ele[],int size);
bool insert(T ele);
void inorder();
void preorder();
void postorder();
};
template<typename T>
bitree<T>::bitree(T ele[],int size){
root=new treenode<T>(ele[0]);
this->size=size;;
for(int i=0;i<size;i++)
insert(ele[i]);}
template<typename T>
bool bitree<T>::insert(T ele){
if(root==NULL) root=new treenode<T>(ele);
else{
treenode<T>*parent=NULL;
treenode<T>*p=root;
while(p!=NULL){
if(ele<p->ele){parent=p;
p=p->left;}//新元素小于父节点,则做父节点左孩纸
else if(ele>p->ele){parent=p;p=p->right;}//新元素大于父节点,则做父节点右边孩纸
else return false;}
if(ele<parent->ele)parent->left=new treenode<T>(ele);
else parent->right=new treenode<T>(ele);
}
size++;
return true;}
template<typename T>
void bitree<T>::inorder(){
inorder(root);}
template<typename T>
void bitree<T>::inorder(treenode<T>*root){
if(root==NULL) return;
else{
inorder(root->left);
cout<<root->ele<<" ";
inorder(root->right);
}
}//中序遍历,递归
template<typename T>
void bitree<T>::preorder(){
preorder(root);}
template<typename T>
void bitree<T>::preorder(treenode<T>*root){
if(root==NULL)return;
else{
cout<<root->ele<<" ";
preorder(root->left);
preorder(root->right);}}//前序遍历
template<typename T>
void bitree<T>::postorder(){
postorder(root);}
template<typename T>
void bitree<T>::postorder(treenode<T>*root){
if(root==NULL) return;
else{postorder(root->left);
postorder(root->right);
cout<<root->ele<<" ";
}}//后序
二叉树插入:若二叉树为空,直接创建一个root节点;若非空,则进行比较:当插入节点小于父节点时候,则作为左节点,反之则是右节点。否则相等时说明二叉树已含此数字不插入;
遍历:通过递归思想,输出父节点,递归左子树和右子树;
堆也是一种二叉树,满足:
是完全二叉树(倒数一层外都是满,最后一层即使不满也是左满);
是满足父节点大于任意子节点,则根节点是最大值;
堆和优先队列虽然都是二叉树结构,也可以由二叉树表示,但是不及数组或者向量表示,尤其是面对数目未定增删多的时候,向量首选:当前节点处于向量或者数组i位置时候,其父节点在(i-1)/2,左右节点在2*i+1,2*i+2位置处。堆的重点在于删除和插入:
template<typename T>
class heap{
private:
vvs<T> vv;
public:
heap();
heap(T ele[],int size);
T remove();
void add(T ele);
int getsi();};
template<typename T>
heap<T>::heap(){}
template<typename T>
heap<T>::heap(T ele[],int size){
for(int i=0;i<size;i++)
add(ele[i]);}
template<typename T>
T heap<T>::remove(){
if(vv.getsize()==0) throw runtime_error("empty");
T tep=vv[0];
vv[0]=vv[vv.getsize()-1];//change last and first
vv.pop_back();//delete the root
int i=0;//root
while(i<vv.getsize()){
int le=2*i+1;
int ri=2*i+2;
if(le>=vv.getsize())break;//tree is heap
int max=le;
if(ri<vv.getsize()){if(vv[max]<vv[ri]) max=ri;}
if(vv[i]<vv[max]){T t=vv[max];vv[max]=vv[i];vv[i]=t;i=max;//返回下一层
}
else break;
}
return tep;
}
template<typename T>
void heap<T>::add(T ele){
vv.push_back(ele);//将新节点添加到堆尾
int i=vv.getsize()-1;
while(i>0){
int pa=(i-1)/2;
if(vv[i]>vv[pa]){T te=vv[i];vv[i]=vv[pa];vv[pa]=te;}
else break;//now is heap
i=pa;//返回上一层
}}
template<typename T>
int heap<T>::getsi(){
return vv.getsize();}
优先队列不同于普通队列,普通队列FIFO,利用链表即可实现,是线性结构;而优先队列是层次结构,需通过堆实现:最大先出的原则:
template<typename T>
class prique{
private:
heap<T> hea;
public:
void enque(T ele){hea.add(ele);}
T deque(){return hea.remove();}//最大先出。满足优先队列定义
int getsiz(){return hea.getsi();}};
下面给出一个利用优先队列模拟病人排队的例子:
class patient{
private:
string name;
int level;
public:
patient(){};
patient(string s,int le){
name=s;level=le;}
bool operator<(patient&q){
return (level<q.level);}
bool operator>(patient&q){
return !(level<q.level);}
string getname(){return name;}
int getle(){return level;}};
int main(){
prique<patient> pp;
pp.enque(patient("zhang",2));
pp.enque(patient("zhao",1));
pp.enque(patient("Tim",5));
pp.enque(patient("cindi",7));
while(pp.getsiz()>0){
patient t=pp.deque();
cout<<"level:"<<t.getle()<<" the name: "<<t.getname()<<endl;
}
system("pause");
return 0;}