在描述二叉树,堆,优先队列之前之前,有必要介绍节点和向量,在二叉树,队,优先队列中会有使用:
二叉树可以用链接在一起的一个个节点描述,每个节点包含一个数据元素和指向左右子树的指针,可以用类定义节点(结构体也可以):
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;}