树基础总结(算法导论)

0.树的存储结构

a.当孩子个数有限时(全部单列出来)

struct Tree
{
	int data;
	Tree *ltree;
	Tree *rtree;
};
b.当孩子个数不定时(将孩子节点连在一起)
struct Node
{
	int data;
	vector<int> child;//孩子节点的序号 
};
vector<Node> tree; 
c.对于完全/满二叉树(可以利用,父亲节点序号i,左孩子2i,右孩子2i+1的性质)
vector<int> data;//data[0]不存储元素 

d.类似于并查集

vector<Node> node;//整棵树的节点 
node[childId]=fatherId; 

1.树的遍历

a.前、中、后序遍历

struct Tree
{
	int data;
	Tree *ltree;
	Tree *rtree; 
};
//前序
void preTraversal(Tree *head)
{
	if(head==NULL)
	{
		return;
	}
	cout<<head->data;
	preTraversal(head->ltree);
	preTraversal(head->rtree);
} 
//中序
void preTraversal(Tree *head)
{
	if(head==NULL)
	{
		return;
	}
	preTraversal(head->ltree);
	cout<<head->data;
	preTraversal(head->rtree);
}
//后序
void preTraversal(Tree *head)
{
	if(head==NULL)
	{
		return;
	}
	preTraversal(head->ltree);
	preTraversal(head->rtree);
	cout<<head->data;
}
b.广、深度遍历
struct Tree
{
	int data;
	vector<Tree*> chlid;
};
//广度优先
void breadthTraversal(Tree *head)
{
	queue<Tree*> q;
	q.push(head);
	while(!q.empty())
	{
		Tree *temp=q.front();
		q.pop();
		cout<<temp->data;
		for(int i=0;i<temp->chlid.size();i++)
		{
			q.push(temp->chlid[i]);
		}
	}
}
//深度优先
void depthTraversal(Tree *head)
{
	if(head==NULL)
	{
		return;
	}
	cout<<head->data;
	for(int i=0;i<head->chlid.size();i++)
	{
		depthTraversal(temp->chlid[i]);
	}
} 

2.树的应用

a.二叉查找树

#include<iostream>
#include<stack>
#include<queue>
using namespace std;

template<class Type>
struct Node
{
	Type cdata;
	Node *lchild;
	Node *rchild;
	Node(Type temp):cdata(temp),lchild(NULL),rchild(NULL){}//跟类初始化列表一样 
};

template<class T>
class BSTree
{
	private:
		Node<T> *root;//这里写Node *root是错的,需要给结构体模板传参Node<T> *root
		void destroy();
	public:
		BSTree():root(NULL){} //构造函数,初始化 
		BSTree(BSTree &c)//重载复制构造函数,防止浅拷贝 
		{
			if(c.getRoot()!=NULL)
			{
				queue< Node<T>* > n1;
				queue< Node<T>* > n2;
				root=new Node<T>(c.getRoot()->cdata);
				Node<T> *storage=root;
				n1.push(c.getRoot());
				n2.push(root);
				while(!n1.empty())//层次 
				{
					Node<T> *te=n1.front();
					Node<T> *te2=n2.front();
					n1.pop();
					n2.pop();
					if(te->lchild!=NULL)
					{
						te2->lchild=new Node<T>(te->lchild->cdata);
						n1.push(te->lchild);
						n2.push(te2->lchild);
					}
					if(te->rchild!=NULL)
					{
						te2->rchild=new Node<T>(te->rchild->cdata);
						n1.push(te->rchild);
						n2.push(te2->rchild);
					}
				}	
				root=storage;
			}	
		}
		~BSTree()//析构函数,回收内存 
		{
			destroy();
		}
		Node<T>* getRoot()
		{
			return root;
		}
		bool insertNode(Node<T>* t);//增 
		Node<T> *findX(T x)const;//查 
		void removeX(T x);//删 
		void midtraversal(Node<T> *te)const;
		bool isTreeEmpty()const;
};

template<class T>
inline bool BSTree<T>::insertNode(Node<T> *t)
{
	if(root==NULL)
	{
		root=t;
		return true;
	}
	Node<T> *te=root;
	while(true)
	{
		if(t->cdata<root->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<class T>
inline void BSTree<T>::destroy()
{
	cout<<"delete: ";
	if(root==NULL)
	{
		return;
	}
	stack< Node<T>* > s;
	s.push(root);
	while(!s.empty())//先序 
	{
		if(s.top()->lchild==NULL&&s.top()->rchild==NULL)
		{		
			Node<T> *te=s.top();
			s.pop();
			cout<<te->cdata<<"\t"; 
			delete te;
		}
		else
		{
			while(s.top()->lchild!=NULL)
			{
				Node<T> *te=s.top()->lchild;
				s.top()->lchild=NULL;
				s.push(te);
			}
			while(s.top()->rchild!=NULL)
			{
				Node<T> *te=s.top()->rchild;
				s.top()->rchild=NULL;
				s.push(te);
			}
		}
	}
}

template<class T>
inline bool BSTree<T>::isTreeEmpty()const//BSTree::isTreeEmpty()是错的,类模板需要传参BSTree<T>::isTreeEmpty() 
{
	if(root==NULL)
	{
		return true;
	}	
	return false;
}  

template<class T>
inline void BSTree<T>::midtraversal(Node<T> * te)const//中序 
{
	if(te==NULL)
	{
		return;
	}
	BSTree<T>::midtraversal(te->lchild);
	cout<<te->cdata<<"(ad: "<<te<<")"<<"\t";
	BSTree<T>::midtraversal(te->rchild);	
}

template<class T>
inline Node<T>* BSTree<T>::findX(T x)const
{
	Node<T>* storage=root;
	while(storage!=NULL)
	{
		if(x<storage->cdata)
		{
			storage=storage->lchild;
		}
		else if(x>storage->cdata)
		{
			storage=storage->rchild;
		}
		else
		{
			return storage;
		}
	}
	return storage;
}

template<class T>
inline void BSTree<T>::removeX(T x)
{
	Node<T> *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<T> *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<int> a;
		for(int i=0;i<10;i++)
		{
			a.insertNode(new Node<int>(c[i]));
		}
		a.midtraversal(a.getRoot());
		cout<<endl;
		a.removeX(9);
		a.midtraversal(a.getRoot());
		cout<<endl;
		if(a.findX(9)!=NULL)
		{
			cout<<a.findX(9)->cdata<<endl;
		}
		else
		{
			cout<<"can't find"<<endl;
		}
		BSTree<int> b(a);
		b.midtraversal(b.getRoot());
		cout<<endl;
	}
	return 0;
}

思考:在构建二叉查找树之前,对输入数据进行洗牌,会不会更好。但是如果数据不是像例子中一次性给出,那么也就没办法对输入数据进行洗牌了。
b.红黑树

是一种二叉查找树,也是一种平衡树。典型的用途是实现关联数组(c++中的map,set ?)

节点数据结构如下:

template<class Type>
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<iostream>
#include<queue>
using namespace std;

template<class Type>
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 T>
class RBTree//析构函数和复制构造函数不再重载,大致与二叉查找树相同 
{
	private:
		Node<T>* root;
		Node<T>* findInsertPos(T x);//寻找插入点 
		bool judgeLR(T x,Node<T> *y);//判断是左孩子(true)还是右孩子(false) 
		void adjust(Node<T> *x);//插入节点之后需要调整 
	public:
		Node<T>* getRoot()
		{
			return root;
		}
		void insertNode(T x);
		void midtraversal(Node<T> *te)const;
		void leveltraversal()const;
};

template<class T>
inline void RBTree<T>::leveltraversal()const
{
	cout<<"层次:"<<endl;
	Node<T> *storage=root;
	queue< Node<T>* > q;
	q.push(storage);
	while(!q.empty())
	{
		Node<T> * te=q.front();
		q.pop();
		cout<<te->val<<"("<<te->color<<")\t";
		if(te->lchild!=NULL)
		{
			q.push(te->lchild);
		}
		if(te->rchild!=NULL)
		{
			q.push(te->rchild);
		}
	}
	cout<<endl;
}

template<class T>
inline void RBTree<T>::midtraversal(Node<T> *te)const
{
	if(te==NULL)
	{
		return;
	}
	RBTree<T>::midtraversal(te->lchild);
	cout<<te->val<<"("<<te->color<<")"<<"\t";
	RBTree<T>::midtraversal(te->rchild);
}

template<class T>
inline bool RBTree<T>::judgeLR(T x,Node<T> *y)
{
	if(x<y->val)
	{
		return true;
	}
	else
	{
		return false;
	}
}

template<class T>
inline Node<T>* RBTree<T>::findInsertPos(T x)
{
	Node<T> *storage=root;
	while(storage!=NULL)
	{
		if(x<storage->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<class T>
inline void RBTree<T>::adjust(Node<T> *x)	 
{
	if(x==root)//< 距离root0,就会直接返回了 >
	{
		x->color=false;
		return;
	}
	if(!x->parent->color)//情况三:父亲节点是黑色的,无需操作,直接返回<  距离root1,因为root是黑的,所以到此步也就返回了 > 
	{
		return;
	}
	//< 距离root2以上距离的,才会做以下操作,所以x->father->father不会非法 > 
	Node<T> *f=x->parent;
	Node<T> *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<T> *g=f->parent;
	Node<T> *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<class T>
inline void RBTree<T>::insertNode(T x)
{
	if(root==NULL)//情况一:树空,根节点为黑 
	{
		root=new Node<T>(x);
		root->color=false;
		return;
	}
	if(findInsertPos(x)==NULL)//情况二:已经存在,无需插入,直接返回 
	{
		return;
	}
	
	/*****下列情况,考查的只有,本身、爹、叔叔的颜色、左右身份*****/ 
	/*****下列情况,先插入,再调整*****/
	Node<T> *f=findInsertPos(x);
	bool mfchild=judgeLR(x,f);
	if(mfchild)
	{
		f->lchild=new Node<T>(x);
		f->lchild->parent=f;
		adjust(f->lchild);
	} 
	else
	{
		f->rchild=new Node<T>(x);
		f->rchild->parent=f;
		adjust(f->rchild);
	}
}
int main()
{
	int c[]={3,9,-1,4,2,7,91,19,10,12,9};
	RBTree<int> a;
	for(int i=0;i<11;i++)
	{
		a.insertNode(c[i]);
	}
	cout<<"中序:"<<endl; 
	a.midtraversal(a.getRoot());
	cout<<endl;
	a.leveltraversal();
	return 0;
}

红黑树与严格的AVL树(高度平衡二叉树)(考研时数构考过)的比对:

转自:http://blog.csdn.net/klarclm/article/details/7780319

相对于AVL树而言,红黑树牺牲了严格的高度平衡的优越条件为 代价,红黑树能够以O(log n)的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不平衡都会在三次旋转之内解决(参看插入操作)。当然,还有一些更好的,但实现起来更复杂的数据结构 能够做到一步旋转之内达到平衡,但红黑树能够给我们一个比较“便宜”的解决方案。

当然,红黑树并不适应所有应用树的领域。如果数据基本上是静态的,那么让他们待在他们能够插入,并且不影响平衡的地方会具有更好的性能。如果数据完全是静态的,例如,做一个哈希表,性能可能会更好一些。

c.哈夫曼树(编码)

#include<iostream>
#include<cstdlib>
#include<queue>
using namespace std;
struct Tree
{
	Tree* lchlid;
	Tree* rchlid;
	int f;//频率
	int data;//值 
};
struct cmp//与sort的cmp不同,结构体中重载调用操作符。也可以重载>操作符,priority_queue<Node,vector<Node>,greater<Node>> 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<Tree,vector<Tree>,cmp> &q) 
{
	while(q.size()>=2)
	{/*
		Tree a=q.top();
		cout<<"&a="<<&a<<endl;//局部变量的地址每次申请的都是相同的,所以后续错了
		q.pop();
		Tree b=q.top();
		cout<<"&b="<<&b<<endl;/花了2个多小时查的,此处需注意了
		q.pop();
		Tree c;
		c.lchlid=&a;
		c.rchlid=&b;
		c.f=a.f+b.f;
		c.data=-1;
		q.push(c);*/
		Tree *a=new Tree;
		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<Tree,vector<Tree>,cmp> &p)
{
	queue<Tree*> 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<<temp->f<<"\t";
		}
		if(temp->lchlid!=NULL)
		{
			q.push(temp->lchlid);
		}
		if(temp->rchlid!=NULL)
		{
			q.push(temp->rchlid);
		}
	}
}
int main()
{
	int num;
	priority_queue<Tree,vector<Tree>,cmp> q;
	cin>>num;
	for(int i=0;i<num;i++)
	{
		Tree temp;
		temp.lchlid=NULL;
		temp.rchlid=NULL;
		temp.f=random(0,1000);
		temp.data=random(1,50);
		q.push(temp);
		cout<<temp.f<<"\t";
	}
	cout<<endl;
	HuffmanTree(q); 
	LT(q);
}

d.并查集

初始化,查根,合并代码如下

#include<iostream>
#define Maxv 200
using namespace std;
int data[Maxv];
//data[i]=j,元素i的祖宗是j
void initial(int data[])//初始化
{
	for(int i=0;i<Maxv;i++)
	{
		data[i]=i;//初始化自己是自己的祖宗 
	}
} 

int findFather(int data[],int x)//查祖宗
{
	if(data[x]==x)
	{
		return x;//自己是自己的祖宗,返回 
	}
	return findFather(data,data[x]);//把自己的爹,做参数,继续寻找 
}

void Union(int data[],int x,int y)//合并两个集合
{
	int xggfather=findFather(data,x);//找到x的祖宗
	int yggfather=findFather(data,y);//找到y的祖宗 
	data[xggfather]= yggfather;//把y的祖宗作为x祖宗的祖宗 
}
并查集应用实例:畅通工程,Kruscal最小生成树

Kruscal最小生成树:

#include<iostream>
#include<queue>
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<Node,vector<Node>,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<vn;i++)
 	 {
 		for(j=i+1;j<=vn;j++)
 		{
			if(dis[i][j])//如果边权值不为0,即顶点之间有边,压入该边 
			{
				Node e;
 				e.v1=i;
  				e.v2=j;
	  			e.len=dis[i][j];
	  			Q.push(e);
	  		}
  		}
    }
    sum=0;
    while(Q.size()!=0)
    {
  		Node e;
   		e=Q.top();
   		Q.pop();
  	 	if(Getfa(e.v1)!=Getfa(e.v2))//若两个顶点不属于同一个点集,表示该边不是回路;也即两个节点的根节点是否相同 
  	 	{                           //根节点不同,则为不同集合,不构成回路  
    		sum=sum+e.len;
    		fa[Getfa(e.v2)]=Getfa(e.v1);//把e.v1的根节点作为e.v2的根节点的爹,也即合并两个集合 
 	    }
   	 }
   	 cout<<sum;
 	 return 0;    
}





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值