Go语言数据结构和算法(三十一)Kruskal算法

90 阅读3分钟

Kruskal算法用于为给定图生成最小生成树.Kruskal算法按边权重的递增顺序对所有

边进行排序..并且仅当所选边不形成任何循环时才继续向树中添加节点.此外.它首先

选择权重最低的边.最后选择权重最高的边.可以说Kruskal算法作出了局部最优的选

择.旨在找到全局最优解.

1.步骤:

按权重的非递减顺序对所有边进行排序.

选择权重最小的边.检查它是否与目前形成的生成树形成循环.如果为形成循环.则添加

到树中.否则.丢弃它.

重复步骤2.直到生成树中有v-1条边.当生成树中有v-1条边时.则形成最小生成树.

2.使用场景:

网络设计:

该算法可用于设计高效且具有成本效益的通信网络.如有线电视网络 互联网络和电话

网络等.

交通网络:

该算法可用于寻找交通网络(如公路 铁路和航空)的最佳路线.

电路设计:

该算法可用于设计电路板和接线图.其中需要优化不同组件之间的连接以最小化总成本.

资源分配:

该算法可用于分配网络或系统资源.如分配带宽 分配存储空间或将任务分配给处理器.

3.实现:

3.1方法:

package data

import "sort"

// 加权图边.
type Edge struct {
	Src  int
	Dst  int
	Cost int
}

func Kruskal(v int, es []Edge) (int, []Edge) {
	sort.Slice(es, func(i, j int) bool {
		return es[i].Cost < es[j].Cost
	})
	cost := 0
	mst := make([]Edge, 0)
	u := newSet(v)
	for _, e := range es {
		if !u.IsSameSet(e.Src, e.Dst) {
			u.Union(e.Src, e.Dst)
			mst = append(mst, e)
			cost += e.Cost
		}
	}
	return cost, mst
}

type UnionSet struct {
	p []int
	r []int
}

// 给定的两个元素包含在一个集合中.则返回true.
func (uf *UnionSet) IsSameSet(a, b int) bool {
	a = uf.find(a)
	b = uf.find(b)
	return a == b
}

func (uf *UnionSet) Union(a, b int) {
	if uf.r[a] > uf.r[b] {
		uf.p[b] = a
	} else {
		uf.p[a] = b
		if uf.r[a] == uf.r[b] {
			uf.r[a]++
		}
	}
}

// 查找返回根元素.
func (uf *UnionSet) find(x int) int {
	if uf.p[x] != x {
		uf.p[x] = uf.find(uf.p[x])
	}
	return uf.p[x]
}

func newSet(size int) *UnionSet {
	uf := new(UnionSet)
	uf.p = make([]int, size)
	uf.r = make([]int, size)
	for i := range uf.p {
		uf.p[i] = i
		uf.r[i] = 1
	}
	return uf
}

3.2main方法:

func main() {
	edges := []data.Edge{{0, 1, 10},
		{0, 2, 6},
		{0, 3, 5},
		{1, 3, 15},
		{2, 3, 4}}
	v := 5
	res1, res2 := data.Kruskal(v, edges)
	fmt.Println(res1, res2)
}

4.实战:

4.1方法:

func FindCriticalAndPseudo(n int, edges [][]int) [][]int {
	//将索引添加到切片.
	for i := range edges {
		edges[i] = append(edges[i], i)
	}
	sort.Slice(edges, func(i, j int) bool {
		return edges[i][2] < edges[j][2]
	})
	//使用Kruskal查找mst.
	minWeight := mstWeight(n, edges, -1, -1)
	critical := make([]int, 0)
	pseudoCritical := make([]int, 0)
	for i := range edges {
		wgt := mstWeight(n, edges, i, -1)
		if wgt == -1 || wgt > minWeight {
			critical = append(critical, edges[i][3])
			continue
		}
		wgt = mstWeight(n, edges, -1, i)
		if wgt == minWeight {
			pseudoCritical = append(pseudoCritical, edges[i][3])
		}
	}
	sort.Ints(critical)
	sort.Ints(pseudoCritical)
	return [][]int{critical, pseudoCritical}
}

func mstWeight(n int, edges [][]int, skipIdx int, forceIncludeIndex int) int {
	dsu := newUnionSet(n)
	var weight int
	if forceIncludeIndex != -1 {
		e := edges[forceIncludeIndex]
		dsu.union(e[0], e[1])
		weight += e[2]
	}
	for i, edge := range edges {
		if i == skipIdx {
			continue
		}
		a, b := edge[0], edge[1]
		ra, rb := dsu.find(a), dsu.find(b)
		if ra == rb {
			continue
		}
		dsu.union(a, b)
		weight += edge[2]
	}
	a := dsu.find(0)
	for i := range dsu.parent[1:] {
		if dsu.find(1+i) != a {
			return -1
		}
	}
	return weight
}

func newUnionSet(n int) *NewUnionSet {
	dsu := &NewUnionSet{
		parent: make([]int, n),
		size:   make([]int, n),
	}
	for i := 0; i < n; i++ {
		dsu.parent[i] = i
		dsu.size[i] = 1
	}
	return dsu
}

type NewUnionSet struct {
	parent []int
	size   []int
}

func (d *NewUnionSet) find(a int) int {
	if d.parent[a] == a {
		return a
	}
	root := d.find(d.parent[a])
	d.parent[a] = root
	return root
}

func (d *NewUnionSet) union(a, b int) {
	a = d.find(a)
	b = d.find(b)
	if a != b {
		if d.size[a] < d.size[b] {
			a, b = b, a
		}
		d.parent[b] = a
		d.size[a] += d.size[b]
	}
}

4.2main方法:

func main() {
	edges := [][]int{{0, 1, 1},
		{1, 2, 1},
		{2, 3, 2},
		{0, 3, 2},
		{0, 4, 3},
		{3, 4, 3},
		{1, 4, 6}}
	n := 5
	pseudo := data.FindCriticalAndPseudo(n, edges)
	fmt.Println(pseudo)
}

字字皆凉意.





如果大家喜欢我的分享的话.可以关注我的微信公众号

念何架构之路