0.树的存储结构
1.树的遍历a.当孩子个数有限时(全部单列出来)
b.当孩子个数不定时(将孩子节点连在一起)struct Tree { int data; Tree *ltree; Tree *rtree; };
c.对于完全/满二叉树(可以利用,父亲节点序号i,左孩子2i,右孩子2i+1的性质)struct Node { int data; vector
child;//孩子节点的序号 }; vector tree; vector
data;//data[0]不存储元素 d.类似于并查集
vector
node;//整棵树的节点 node[childId]=fatherId;
a.前、中、后序遍历
b.广、深度遍历struct Tree { int data; Tree *ltree; Tree *rtree; }; //前序 void preTraversal(Tree *head) { if(head==NULL) { return; } cout<
data; preTraversal(head->ltree); preTraversal(head->rtree); } //中序 void preTraversal(Tree *head) { if(head==NULL) { return; } preTraversal(head->ltree); cout< data; preTraversal(head->rtree); } //后序 void preTraversal(Tree *head) { if(head==NULL) { return; } preTraversal(head->ltree); preTraversal(head->rtree); cout< data; } struct Tree { int data; vector
chlid; }; //广度优先 void breadthTraversal(Tree *head) { queue q; q.push(head); while(!q.empty()) { Tree *temp=q.front(); q.pop(); cout< data; for(int i=0;i chlid.size();i++) { q.push(temp->chlid[i]); } } } //深度优先 void depthTraversal(Tree *head) { if(head==NULL) { return; } cout< data; for(int i=0;i chlid.size();i++) { depthTraversal(temp->chlid[i]); } }
2.树的应用
a.二叉查找树
#include
#include #include using namespace std; template struct Node { Type cdata; Node *lchild; Node *rchild; Node(Type temp):cdata(temp),lchild(NULL),rchild(NULL){}//跟类初始化列表一样 }; template class BSTree { private: Node *root;//这里写Node *root是错的,需要给结构体模板传参Node *root void destroy(); public: BSTree():root(NULL){} //构造函数,初始化 BSTree(BSTree &c)//重载复制构造函数,防止浅拷贝 { if(c.getRoot()!=NULL) { queue< Node * > n1; queue< Node * > n2; root=new Node (c.getRoot()->cdata); Node *storage=root; n1.push(c.getRoot()); n2.push(root); while(!n1.empty())//层次 { Node *te=n1.front(); Node *te2=n2.front(); n1.pop(); n2.pop(); if(te->lchild!=NULL) { te2->lchild=new Node (te->lchild->cdata); n1.push(te->lchild); n2.push(te2->lchild); } if(te->rchild!=NULL) { te2->rchild=new Node (te->rchild->cdata); n1.push(te->rchild); n2.push(te2->rchild); } } root=storage; } } ~BSTree()//析构函数,回收内存 { destroy(); } Node * getRoot() { return root; } bool insertNode(Node * t);//增 Node *findX(T x)const;//查 void removeX(T x);//删 void midtraversal(Node *te)const; bool isTreeEmpty()const; }; template inline bool BSTree ::insertNode(Node *t) { if(root==NULL) { root=t; return true; } Node *te=root; while(true) { if(t->cdata cdata) { if(root->lchild!=NULL) { root=root->lchild; } else { root->lchild=t; root=te; return true; } } else if(t->cdata>root->cdata) { if(root->rchild!=NULL) { root=root->rchild; } else { root->rchild=t; root=te; return true; } } else { root=te; return false; } } } template inline void BSTree ::destroy() { cout<<"delete: "; if(root==NULL) { return; } stack< Node * > s; s.push(root); while(!s.empty())//先序 { if(s.top()->lchild==NULL&&s.top()->rchild==NULL) { Node *te=s.top(); s.pop(); cout< cdata<<"\t"; delete te; } else { while(s.top()->lchild!=NULL) { Node *te=s.top()->lchild; s.top()->lchild=NULL; s.push(te); } while(s.top()->rchild!=NULL) { Node *te=s.top()->rchild; s.top()->rchild=NULL; s.push(te); } } } } template inline bool BSTree ::isTreeEmpty()const//BSTree::isTreeEmpty()是错的,类模板需要传参BSTree ::isTreeEmpty() { if(root==NULL) { return true; } return false; } template inline void BSTree ::midtraversal(Node * te)const//中序 { if(te==NULL) { return; } BSTree ::midtraversal(te->lchild); cout< cdata<<"(ad: "< ::midtraversal(te->rchild); } template inline Node * BSTree ::findX(T x)const { Node * storage=root; while(storage!=NULL) { if(x cdata) { storage=storage->lchild; } else if(x>storage->cdata) { storage=storage->rchild; } else { return storage; } } return storage; } template inline void BSTree ::removeX(T x) { Node *te=findX(x); if(te==NULL)//情况一:不存在x,直接返回 { return; } if(te->lchild==NULL&&te->rchild==NULL)//情况二:叶子节点,直接删 { delete te;//调用指针的析构函数,来释放对象内存 return; } if(te->lchild==NULL&&te->rchild!=NULL)//情况三:单子树,直接删,让子树代替原节点 { te->cdata=te->rchild->cdata; te->lchild=te->rchild->lchild; te->rchild=te->rchild->rchild; delete te->rchild; return; } if(te->lchild!=NULL&&te->rchild==NULL) { te->cdata=te->lchild->cdata; te->lchild=te->lchild->lchild; te->rchild=te->lchild->rchild; delete te->lchild; return; } if(te->lchild!=NULL&&te->rchild!=NULL)//情况四:双子树,让直接后继代替原节点,直接后继是叶子节点直接删,有单子树,按上操作 { //找直接后继,如找9的直接后继,就是找9的右子树中比9大的最小数 Node *te2=te->rchild; while(te2->lchild!=NULL) { te2=te2->lchild; } //直接后继代替原节点 te->cdata=te2->cdata; //删除直接后继 if(te2->rchild==NULL) { delete te2; return; } if(te2->rchild!=NULL) { te2->cdata=te2->rchild->cdata; te2->lchild=te2->rchild->lchild; te2->rchild=te2->rchild->rchild; delete te2->rchild; return; } } } int main() { { int c[]={3,9,-1,4,2,7,9,19,10,12}; BSTree a; for(int i=0;i<10;i++) { a.insertNode(new Node (c[i])); } a.midtraversal(a.getRoot()); cout< cdata< b(a); b.midtraversal(b.getRoot()); cout< 思考:在构建二叉查找树之前,对输入数据进行洗牌,会不会更好。但是如果数据不是像例子中一次性给出,那么也就没办法对输入数据进行洗牌了。
b.红黑树是一种二叉查找树,也是一种平衡树。典型的用途是实现关联数组(c++中的map,set ?)
节点数据结构如下:
成为一颗红黑树的条件:template
struct Node { Type val;//节点存储数据 bool color;//true为红,false为黑 Node* lchild; Node* rchild; Node* parent; Node(Type temp):val(temp),color(true),lchild(NULL),rchild(NULL),parent(NULL){} };
1.节点非黑即红(对于分析没有实际意义)
2.根节点是黑色的(说不出来,但写插入时,很有用,情况四再次向上调整时有用,父亲节点是红色时,一定存在祖父节点的判定...模糊)
3.叶节点(空节点)是黑色的(为方便代码编写和算法实现)
4.红节点的子节点是黑色的(并不意味着每条路径上的节点都是黑红交替的)
5.对于每个节点,从该节点到其子孙叶节点(空节点)的所有路径上包含的黑节点数目是相同的
红黑树的平衡性讨论:
由条件5可知:因为从一点出发,任何一条路径到叶节点所经历的黑节点个数是相同的,所以一条路径上的红节点个数越多,路径也就越长
由条件4和上条推论可知:因为红节点的子节点一定是黑的,意味着两个红节点是不可能连续,所以红节点最多时,就是在每两个黑节点之间插上1个,所以红节点最多与黑节点个数相同
由上可推出:
最长路径是红黑交替出现长度为2n(n为黑节点个数)
最短路径是纯黑节点长度为n
最长路径最多是最短路径的两倍,这保证了这棵树的平衡性。(当然对比严格的平衡二叉树来说,平衡性肯定是要差些的)
红黑树的插入:(中心思想:插入新节点,不能破坏性质1~5,这样才能保证树的平衡)
写的很好:http://www.cnblogs.com/fornever/archive/2011/12/02/2270692.html
情况1:根节点为空,插入节点设为根节点,color设为黑
情况2:已经存在该节点,直接返回
情况3:父亲节点为黑,插入后,无需调整,直接返回
情况4:父亲节点为红,且叔叔节点也为红,插入后,将父亲节点设为黑,叔叔节点设为黑,祖父节点(父亲节点为红,则至少有祖父root为黑)设为红,将祖父节点作为新节点,再次向上调整
情况5:父亲节点为红,叔叔节点为黑,父亲节点为左(右)孩子,新节点也为左(右)孩子,右(左)单旋,原父亲节点站在了原祖父节点的位置上,原父亲节点转黑,原祖父节点转红
情况6:父亲节点为红,叔叔节点为黑,父亲节点为左(右)孩子,新节点为右(左)孩子,左(右)旋,各节点颜色不变。现在成了情况5,转情况5处理
红黑树的删除:(中心思想:删除某个节点,要保证黑节点个数不变,这样性质5才不会被破坏)
写的很好:http://blog.csdn.net/spch2008/article/details/9338923
最重要的一点,以下结构会破坏性质5,所以不会出现(盗图):
详细参看上述博客
红黑树代码:(仅插入部分,删除真的好麻烦!)
#include
#include using namespace std; template struct Node { Type val;//节点存储数据 bool color;//true为红,false为黑 Node* lchild; Node* rchild; Node* parent; Node(Type x):val(x),color(true),lchild(NULL),rchild(NULL),parent(NULL){} }; template class RBTree//析构函数和复制构造函数不再重载,大致与二叉查找树相同 { private: Node * root; Node * findInsertPos(T x);//寻找插入点 bool judgeLR(T x,Node *y);//判断是左孩子(true)还是右孩子(false) void adjust(Node *x);//插入节点之后需要调整 public: Node * getRoot() { return root; } void insertNode(T x); void midtraversal(Node *te)const; void leveltraversal()const; }; template inline void RBTree ::leveltraversal()const { cout<<"层次:"< *storage=root; queue< Node * > q; q.push(storage); while(!q.empty()) { Node * te=q.front(); q.pop(); cout< val<<"("< color<<")\t"; if(te->lchild!=NULL) { q.push(te->lchild); } if(te->rchild!=NULL) { q.push(te->rchild); } } cout< inline void RBTree ::midtraversal(Node *te)const { if(te==NULL) { return; } RBTree ::midtraversal(te->lchild); cout< val<<"("< color<<")"<<"\t"; RBTree ::midtraversal(te->rchild); } template inline bool RBTree ::judgeLR(T x,Node *y) { if(x val) { return true; } else { return false; } } template inline Node * RBTree ::findInsertPos(T x) { Node *storage=root; while(storage!=NULL) { if(x val) { if(storage->lchild!=NULL) { storage=storage->lchild; } else { return storage; } } else if(x>storage->val) { if(storage->rchild!=NULL) { storage=storage->rchild; } else { return storage; } } else { return NULL; } } return storage; } template inline void RBTree ::adjust(Node *x) { if(x==root)//< 距离root0,就会直接返回了 > { x->color=false; return; } if(!x->parent->color)//情况三:父亲节点是黑色的,无需操作,直接返回< 距离root1,因为root是黑的,所以到此步也就返回了 > { return; } //< 距离root2以上距离的,才会做以下操作,所以x->father->father不会非法 > Node *f=x->parent; Node *u; bool fcolor=f->color; bool ucolor; bool fgchild=judgeLR(f->val,f->parent); bool mfchild=judgeLR(x->val,f); if(fgchild) { u=f->parent->rchild; } else { u=f->parent->lchild; } if(u==NULL) { ucolor=false; } else { ucolor=u->color; } if(fcolor&ucolor)//情况四:父亲节点与叔叔节点都是红的,父亲节点和叔叔节点全改为黑,祖父节点改为红,把祖父节点当做新插入节点,继续调整 { f->color=false; u->color=false; f->parent->color=true; adjust(f->parent); return; } bool tag; bool gffchild=false; Node *g=f->parent; Node *gf=g->parent; if(gf==NULL)//g是root { tag=true; } else { tag=false; gffchild=judgeLR(g->val,gf); } if(fgchild&mfchild)//情况五:父亲节点为红,叔叔节点为黑,父亲节点为左,我也为左,即同侧,左单旋 { g->parent=f; g->lchild=f->rchild; g->color=true; f->rchild=g; f->color=false; if(tag) { f->parent=NULL; root=f; return; } else { if(gffchild) { gf->lchild=f; } else { gf->rchild=f; } f->parent=gf; return; } } if((!fgchild)&(!mfchild))//情况五:父亲节点为红,叔叔节点为黑,父亲节点为右,我也为右,即同侧,右单旋 { g->parent=f; g->rchild=f->lchild; g->color=true; f->lchild=g; f->color=false; if(tag) { f->parent=NULL; root=f; return; } else { if(gffchild) { gf->lchild=f; } else { gf->rchild=f; } f->parent=gf; return; } } if(fgchild&(!mfchild))//情况六:父亲节点为红,叔叔节点为黑,父亲节点为左,我为右,单旋一次就成了情况五 { f->rchild=x->lchild; f->parent=x; g->lchild=x; x->parent=g; x->lchild=f; adjust(f); return; } if((!fgchild)&mfchild)//情况六:父亲节点为红,叔叔节点为黑,父亲节点为右,我为左,单旋一次就成了情况五 { f->lchild=x->rchild; f->parent=x; g->rchild=x; x->parent=g; x->rchild=f; adjust(f); return; } } template inline void RBTree ::insertNode(T x) { if(root==NULL)//情况一:树空,根节点为黑 { root=new Node (x); root->color=false; return; } if(findInsertPos(x)==NULL)//情况二:已经存在,无需插入,直接返回 { return; } /*****下列情况,考查的只有,本身、爹、叔叔的颜色、左右身份*****/ /*****下列情况,先插入,再调整*****/ Node *f=findInsertPos(x); bool mfchild=judgeLR(x,f); if(mfchild) { f->lchild=new Node (x); f->lchild->parent=f; adjust(f->lchild); } else { f->rchild=new Node (x); f->rchild->parent=f; adjust(f->rchild); } } int main() { int c[]={3,9,-1,4,2,7,91,19,10,12,9}; RBTree a; for(int i=0;i<11;i++) { a.insertNode(c[i]); } cout<<"中序:"< 红黑树与严格的AVL树(高度平衡二叉树)(考研时数构考过)的比对:
转自:http://blog.csdn.net/klarclm/article/details/7780319
相对于AVL树而言,红黑树牺牲了严格的高度平衡的优越条件为 代价,红黑树能够以O(log n)的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不平衡都会在三次旋转之内解决(参看插入操作)。当然,还有一些更好的,但实现起来更复杂的数据结构 能够做到一步旋转之内达到平衡,但红黑树能够给我们一个比较“便宜”的解决方案。
当然,红黑树并不适应所有应用树的领域。如果数据基本上是静态的,那么让他们待在他们能够插入,并且不影响平衡的地方会具有更好的性能。如果数据完全是静态的,例如,做一个哈希表,性能可能会更好一些。
c.哈夫曼树(编码)
#include
#include #include using namespace std; struct Tree { Tree* lchlid; Tree* rchlid; int f;//频率 int data;//值 }; struct cmp//与sort的cmp不同,结构体中重载调用操作符。也可以重载>操作符,priority_queue ,greater > Q; { bool operator()(Tree a,Tree b) { return a.f>b.f;//>升序,<降序 } }; int random(int start,int end) { return start+rand()%(end-start); } void HuffmanTree(priority_queue ,cmp> &q) { while(q.size()>=2) {/* Tree a=q.top(); cout<<"&a="<<&a< data=q.top().data; a->lchlid=q.top().lchlid; a->rchlid=q.top().rchlid; a->f=q.top().f; q.pop(); Tree *b=new Tree; b->data=q.top().data; b->lchlid=q.top().lchlid; b->rchlid=q.top().rchlid; b->f=q.top().f; q.pop(); Tree c; c.lchlid=a; c.rchlid=b; c.f=a->f+b->f; c.data=-1; q.push(c); } } void LT(const priority_queue ,cmp> &p) { queue q; Tree n=p.top(); q.push(&n); while(!q.empty()) { Tree *temp=q.front(); q.pop(); if(temp->data==-1) { cout<<"Node\t"; } else { cout< f<<"\t"; } if(temp->lchlid!=NULL) { q.push(temp->lchlid); } if(temp->rchlid!=NULL) { q.push(temp->rchlid); } } } int main() { int num; priority_queue ,cmp> q; cin>>num; for(int i=0;i d.并查集
初始化,查根,合并代码如下
并查集应用实例:畅通工程,Kruscal最小生成树#include
#define Maxv 200 using namespace std; int data[Maxv]; //data[i]=j,元素i的祖宗是j void initial(int data[])//初始化 { for(int i=0;i Kruscal最小生成树:
#include
#include using namespace std; #define Maxv 100+5 struct Node { int v2; int v1; int len; }; struct cmp { bool operator()(Node a,Node b) { return a.len>b.len; } }; int dis[Maxv][Maxv];//dis[i][j]等于0时表示不连通 ,不等于1时表示边权值 int fa[Maxv];//father,并查集 int Getfa(int i)//查找根节点的函数 { if(fa[i]!=i)//如果不是根节点 fa[i]=Getfa(fa[i]);//找根节点 return fa[i];//返回节点i所在集合的根节点 } int main() { int sum;//最小生成树代价 priority_queue ,cmp> Q;//声明小顶堆,返回最小数 int vn;//图中的顶点个数 int i; int j; cin>>vn; //输入图 for(i=1;i<=vn;i++) { for(j=1;j<=vn;j++) { cin>>dis[i][j]; } } for(i=1;i<=vn;i++) { fa[i]=i;//并查集,father,一开始有vn个节点,就有vn个集合 } while(!Q.empty()) { Q.pop(); } //把每条边压入堆中 for(i=1;i