树与最小生成树

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Kruskal算法复杂度
• Kruskal算法进行的操作有:一次排序,使 用快排,平均复杂度为 O(log2 m) 。 O • 2m次Find(), (2m log2 n) • n-1次Union(), (n) O • 故Kruskal算法总的时间复杂度为
O(log2 m 2m log2 n n)
并查集优化
• 优化之后,再处理刚才那组数据,就会变 成这样。 2
21 32 43 54 4 1 3 5
• 这样,在后续查找中速度就会是O(1)而不 是最坏O(n)。
Kruskal实现
• 读入m条边,以数组(前向星)的形式按照 边的权值升序储存。从这个数组中依次选 取每条边,如果这条边的两顶点在同一个 联通分量上(即属于同一集),那么舍去 这条边,反之,将其加入MST中。
Kruskal实现
• • • • • • void input() { scanf("%d%d",&n,&m); for(int i=0;i<m;++i) scanf("%d%d%d",&e[i].u,&e[i].v,& e[i].w); } • • • • • • • • • • • • • • • int kruskal() { int sum=0,num=1,u,v; for(int i=0;i<m&&num<n;++i) { u=Find(e[i].u),v=Find(e[i].v); if(u!=v) { sum+=e[i].w; Union(u,v); num++; } } return sum; }
树与最小生成树
百度ID:轩轩醉了 orzwjmzbmr@gmail.com
什么是树?Fra Baidu bibliotek
• 树是有根结点和若干颗子树构成的。树是 由一个集合以及在该集合上定义的一种关 系构成的。集合中的元素称为树的结点, 所定义的关系称为父子关系。父子关系在 树的结点之间建立了一个层次结构。在这 种层次结构中有一个结点具有特殊的地位, 这个结点称为该树的根结点,或称为树根。
Kruskal实现
无向图G
MST
边的数组
Kruskal实现
• • • • • • 1.MST+=(1,6) 2.MST+=(3,4) 3.MST+=(2,7) 4.MST+=(2,3) 5.MST+=(4,5) 6.MST+=(5,6)
Kruskal实现
• • • • • • • • • • • • • •
• 其时间主要取决于边的数量,故kruskal算 法适合稀疏图。在noip级大多数题目中都 是可以用的
Prim算法
• Prim算法的基本思想是以顶点为主导地位: 从起始顶点出发,通过选择当前可用的最 小权值,依次把其他顶点加入到生成树中。 • 具体过程如下: • 选择一个起点v0加入到MST中。 • 以后每次选择一条一个顶点在MST中,一个 顶点不再MST中并且权值最小的边加入MST, 直至n个顶点全部加入到MST中。
• #include<cstdio> • #include<cstdlib> • #define N 10002 • #define M 100002 • using namespace std; • typedef struct{int u,v,w;}E; • E e[M]; • int fa[N],n,m; int cmp(const void *a,const void *b); • • void ufset(); • void Union(int a,int b); • int Find(int x); • void input(); • int kruskal(); • • •
Prim算法实现
• Prim需要两个数组(lowcost[]、nearst[], 后文中简记为low[]和near[]),以及存储 图的邻接矩阵或者链表。 • 记在MST中的点的集合为T,其余为T'。 • lowcost[]:记录T'的顶点到T中的顶点的 距离的最小值。 • nearst[]:记录T'中的顶点到T中的顶点最 近的那个点。-1表示改点在MST中。
并查集
• 并查集要怎么记录呢? • 并查集使用一个大小为N的数组来记录。我 们把它命名为father[N]。它的记录规则是这 样的: • 若一个点i是根节点,那么father[i]=i。 • 若一个点i不是根节点,那么father[i]≠i,且 father[i]为i的父节点的编号。
并查集
• • • • • • • • • • • • • • int Find(int x) { int s,t; for(s=x;father[s]!=s;s=father[s]); return s; } void Union(int a,int b) { int x=Find(a),y=Find(b); if(x==y) return; else father[y]=x; } • • • • • void ufset() { for(int i=0;i<=MAXN;++i) father[i]=i; }
并查集优化
• 这样,对于Find函数要有一点小改动,即
• for(s=x;father[s]>=0;s=father[s]);
• 对于Union函数,修改如下。
void Union(int a,int b) { int x=Find(a),y=Find(b); if(x==y) return; if(father[x]>father[y]) { father[y]+=father[x]; father[x]=y; } else { father[x]+=father[y]; father[y]=x; } }
• 一个有n个结点的连通图的生成树 是原图的极小连通子图,且包含原 图中的所有 n 个结点,并且有保 持图联通的最少的边。
最小生成树算法
• 克鲁斯卡尔(Kruskal)算法 • Boruvka算法 • 普里姆(Prim)算法
构成最小生成树的准则
• 1.必须只使用该网络中的边来构造最小生 成树。 • 2.必须使用且仅使用n-1条边来连接网络中 的n个顶点 • 3.不能使用产生回路的边。
Prim算法实现
• • • • • • • • • • • • • • • • int prim(int v0) { int sum=0; G *temp; for(int i=0;i<=n;++i) { near[i]=v0; low[i]=INT_MAX; } near[v0]=-1; temp=h[v0]; while(temp!=NULL) { low[temp->v]=temp->w; temp=temp->next; } • • • • • • • • • • • • • • • • • • • for(int i=1;i<n;++i) { int v= -1,min=INT_MAX; for(int j=1;j<=n;++j) if(near[j]!=-1&&low[j]<min) {v=j;min=low[j];} sum+=low[v]; near[v]=-1; temp=h[v]; while(temp!=NULL) { if(near[temp->v]!=-1&&temp->w<low[v]) { low[temp->v]=temp->w; near[temp->v]=v; } temp=temp->next; } }
并查集优化
• 2.加权法则 • 之前在father[]数组中,对于根结点i, father[i]=i,现在进行修改。 • 对于根结点i,father[i]<0,并且 |father[i]|为以i为根节点的集合中的元 素个数。 • 例如,对于刚才那组数据,father[5]=-5。 • 之后,每次合并时,都将元素较少的集合 合并到元素较多的集合中去。
注意,并不是在所有题目中都能使用压缩 路径。
并查集优化
• • • • • • • • • • • • int Find(int x) { int s,t; for(s=x;father[s]!=s;s=father[s]); while(s!=x) { t=father[x]; father[x]=s; x=t; } return s; }
int main() { freopen("kruskal.in","r",stdin); input(); ufset(); qsort(e,m,sizeof(E),cmp); printf("%d\n",kruskal()); return 0; } void ufset() { for(int i=0;i<=n;++i) fa[i]=-1; } int cmp(const void *a,const void *b){ return (*(E*)a).w-(*(E*)b).w; }
克鲁斯卡尔(Kruskal)算法
• kruskal算法的基本思想是以边为主导地位, 始终都是选择当前可用的最小权值的边来 构造MST。
• 由于是以边为主导地位构造MST,需要判断 边的两个顶点是否都已经加入到了MST中, 这时就需要使用一种数据结构——并查集。
并查集
• 并查集的作用是判断两个元素是否属于同 一个集合以及合并两个集合。 • 并查集的基本操作有两个:搜索(Find), 合并(Union)。
什么是生成树?
• 生成树是一个连通图G的一个极小连通子图。 • 包含G的所有n个顶点,但只有n-1条边,并 且是连通的。 • 生成树可由遍历过程中所经过的边组成。
什么是最小生成树?
• 最小生成树(Minimum Spannirng Tree), 又称最小代价生成树(Minimum-cost Spannirng Tree)
并查集
• 以为这就行了?不!没那么简单!看下面 这组数据。 21 5 • 可以发现,这棵树变成 3 2 4 了一棵典型的退化树, 4 3 在后续的查找中会变得 5 4 3 非常慢。例如我要查找 2 节点1的根节点,就需 要顺着访问4个点才能 1 找到。
并查集优化
• 1.压缩路径。 • 可以在Find()函数中加一个操作,将当前 查询的点到根节点这条路径上的所有节点 都直接连接到根节点上。
Prim算法实现
• 1.在low[]中选择near[i]!=-1且low[i]最 小的点,记为v。 • 2.near[v]←-1,sum←sum+low[v] • 3.修改low[]。因为T中加入v后,T'中的的 点到T中的点的最小值可能会改变,因此要 比较更新一遍。 • 4.修改near[],原因同上。 • 5.重复1-4直至所有点都在T中。
Prim算法实现
• • • • • • • • • • • • • • • • • • #include<cstdio> #include<cstdlib> #include<climits> #define N 10002 using namespace std; typedef struct node{ int v,w; struct node *next}G; G *g[N]; int low[N],near[N],n,m; void input(); int prim(int v0); int main() { input(); printf("%d\n",prim(1)); return 0; } • • • • • • • • • • • • • • • • • • • void input(){ G *temp; int u,v,w; scanf("%d%d",&n,&m); for(int i=0;i<m;++i) { scanf("%d%d%d",&u,&v,&w); temp=new G; temp->v=v; temp->w=w; temp->next=g[u]; g[u]=temp; temp=new G; temp->v=u; temp->w=w; temp->next=g[v]; g[v]=temp; } }
Kruskal实现
• • • • • • • • • • • • • void Union(int a,int b) { if(fa[a]>fa[b]) { fa[b]+=fa[a]; fa[a]=b; } else { fa[a]+=fa[b]; fa[b]=a; } } • • • • • • • • • • • • int Find(int x) { int s,t; for(s=x;fa[s]>=0;s=fa[s]); while(s!=x) { int t=fa[x]; fa[x]=s; x=t; } return s; }
相关文档
最新文档