数据结构之图源码复习笔记(邻接矩阵、广度搜索、深度搜索、最小生成树...)

原创
2019/04/24 12:35
阅读数 60

graph.go

package maps

import (
	"fmt"
)

/*
涉及对象:
	1. 顶点(Node): 顶点内容、是否访问
	2. 边(Edge): 顶点A-B,权重,是否访问
	3. 图(map): 容量、邻接矩阵、当前顶点个数、顶点集合、边集合

涉及概念:
	顶点集合数量 = 容量
	边集合数量 = 顶点集合数量 - 1
	邻接矩阵大小 = 容量 ^ 2
*/

// Node struct.
type Node struct {
	Data      rune
	IsVisited bool
}

// Edge struct.
type Edge struct {
	NodeIndexA  int
	NodeIndexB  int
	WeightValue int
	IsVisited   bool
}

// CMap struct.
type CMap struct {
	Capacity  int    // 容量
	Matrix    []int  // 邻接矩阵
	NodeCount int    // 节点个数
	NodeArray []Node // 节点数据
	Edge      []Edge // 边数据
}

// CreateEdge .
func CreateEdge(nodeIndexA, nodeIndexB, weightValue int) *Edge {
	return &Edge{
		NodeIndexA:  nodeIndexA,
		NodeIndexB:  nodeIndexB,
		WeightValue: weightValue,
		IsVisited:   false,
	}
}

// CreateCMap impl.
func CreateCMap(capacity int) *CMap {
	return &CMap{
		Capacity:  capacity,
		Matrix:    make([]int, capacity*capacity),
		NodeCount: 0,
		NodeArray: make([]Node, capacity),
		Edge:      make([]Edge, capacity-1),
	}
}

// AddNode impl.
func (cmap *CMap) AddNode(node *Node) bool {
	if node == nil {
		return false
	}
	cmap.NodeArray[cmap.NodeCount].Data = node.Data
	cmap.NodeCount++
	return true
}

// ResetNode impl
func (cmap *CMap) ResetNode() {
	for i := 0; i < len(cmap.NodeArray); i++ {
		cmap.NodeArray[i].IsVisited = false
	}
}

// SetValueToMatrixForDirectedGraph impl.
func (cmap *CMap) SetValueToMatrixForDirectedGraph(row, col, val int) bool {
	if row <= 0 || row >= cmap.Capacity {
		return false
	}
	if col < 0 || col >= cmap.Capacity {
		return false
	}

	cmap.Matrix[row*cmap.Capacity+col] = val
	return true
}

// SetValueToMatrixForUnDirectedGraph impl.
func (cmap *CMap) SetValueToMatrixForUnDirectedGraph(row, col, val int) bool {
	if row < 0 || row >= cmap.Capacity {
		return false
	}
	if col < 0 || col >= cmap.Capacity {
		return false
	}

	cmap.Matrix[row*cmap.Capacity+col] = val
	cmap.Matrix[col*cmap.Capacity+row] = val

	return true
}

// GetWeightValueFromMatrix impl
func (cmap *CMap) GetWeightValueFromMatrix(row, col int) int {
	if row < 0 || row >= cmap.Capacity {
		return 0
	}
	if col < 0 || col >= cmap.Capacity {
		return 0
	}
	return cmap.Matrix[row*cmap.Capacity+col]
}

// PrinMatrix impl.
func (cmap *CMap) PrinMatrix() {
	for i := 0; i < cmap.Capacity; i++ {
		for k := 0; k < cmap.Capacity; k++ {
			fmt.Printf("%d\t", (cmap.Matrix[i*cmap.Capacity+k]))
		}
		fmt.Println()
	}

}

// DepthFirstTraverse impl.
func (cmap *CMap) DepthFirstTraverse(nodeIndex int) {
	fmt.Print(string(cmap.NodeArray[nodeIndex].Data), " ")
	cmap.NodeArray[nodeIndex].IsVisited = true

	for i := 0; i < cmap.Capacity; i++ {
		value := cmap.GetWeightValueFromMatrix(nodeIndex, i)
		if value != 0 {
			if !cmap.NodeArray[i].IsVisited {
				cmap.DepthFirstTraverse(i)
			}
		}
	}

}

// BreadthFirstTraverse impl
func (cmap *CMap) BreadthFirstTraverse(nodeIndex int) {
	fmt.Println(cmap.NodeArray[nodeIndex].Data, " ")
	cmap.NodeArray[nodeIndex].IsVisited = true

	var vector []int
	vector = append(vector, nodeIndex)

	cmap.BreadthFirstTraverseImpl(vector)
}

// BreadthFirstTraverseImpl .
func (cmap *CMap) BreadthFirstTraverseImpl(preVector []int) {
	var vector []int
	for j := 0; j < len(preVector); j++ {
		for i := 0; i < cmap.Capacity; i++ {
			value := cmap.GetWeightValueFromMatrix(preVector[j], i)
			if value != 0 {
				if !cmap.NodeArray[i].IsVisited {
					fmt.Print(string(cmap.NodeArray[i].Data), " ")
					cmap.NodeArray[i].IsVisited = true
					vector = append(vector, i)
				}
			}
		}
	}

	if len(vector) > 0 {
		cmap.BreadthFirstTraverseImpl(vector)
	}
}

// PrimTree (最小生成树 - 普里姆).
// 1. 选择一个顶点,放入已选顶点集合
// 2. 找到顶点对应的边集合,放入待选择集合
// 3. 找到最小权重的边,放入已选边集合
// 4. 找到最小权重边的另外一个顶点B,放入已选顶点集合
// 5. 找到顶点B对应的边集合,放入待选择集合
// 6. 找到最小权重的边,放入已选边集合
// 7. ...
func (cmap *CMap) PrimTree(nodeIndex int) {
	value := 0
	edgeCount := 0
	var nodes []int
	var edges []Edge
	nodes = append(nodes, nodeIndex)
	cmap.NodeArray[nodeIndex].IsVisited = true

	fmt.Println(string(cmap.NodeArray[nodeIndex].Data))

	for edgeCount < cmap.Capacity-1 {

		temp := nodes[len(nodes)-1]
		nodes = nodes[:len(nodes)-1]

		// 1. 找到所有与顶点temp对应的边
		for i := 0; i < cmap.Capacity; i++ {
			value = cmap.GetWeightValueFromMatrix(temp, i)
			if value != 0 {
				if !cmap.NodeArray[i].IsVisited {
					edge := Edge{
						NodeIndexA:  temp,
						NodeIndexB:  i,
						WeightValue: value,
					}
					edges = append(edges, edge)
				}
			}
		}

		// 2. 从可选边集合找到最小的边
		minEdgeIndex := getMinEdge(edges)
		edges[minEdgeIndex].IsVisited = true
		cmap.Edge[edgeCount] = edges[minEdgeIndex]
		edgeCount++

		fmt.Print(edges[minEdgeIndex].NodeIndexA, "---", edges[minEdgeIndex].NodeIndexB, " ")
		fmt.Println(edges[minEdgeIndex].WeightValue)

		// 3. 找出下一个顶点并放入集合
		nextNodeIndex := edges[minEdgeIndex].NodeIndexB
		nodes = append(nodes, nextNodeIndex)
		cmap.NodeArray[nextNodeIndex].IsVisited = true

		fmt.Println(string(cmap.NodeArray[nextNodeIndex].Data))
	}

}

// KruskalTree (最小生成树 - 克鲁斯卡尔)
// 1. 找到所有的边,放入集合SetA
// 2. 选择权重最小的边,并将边放入数组
// 3. 查找权重最小边涉及的顶点(顶点a-b),并将顶点放入数组
// 4. 查找顶点a和b所在的集合
// 5. 根据顶点a和b所在的集合进行如下逻辑处理:
//		1). 如顶点a和b在都不在集合中,则新建集合set,并将顶点a和b放入set集合
//		2). 如顶点a(b)在集合set中,b(a)不在集合中, 则将b(a)放入集合a(b)中
// 		3). 如顶点a和b在不同的集合,则将b集合合并到a集合
//		4). 如顶点a和b在同一集合,则产生回路,不做处理
// 6. 循环步骤2-5【当边未搜索完时】
func (cmap *CMap) KruskalTree() {

	var edgeCount int

	// 定义存放节点集合的数组
	var nodeSets [][]int

	// 一、取出所有的边
	var edges []Edge
	for i := 0; i < cmap.Capacity; i++ {
		for k := i + 1; k < cmap.Capacity; k++ {
			value := cmap.GetWeightValueFromMatrix(i, k)
			if value != 0 {
				edge := Edge{
					NodeIndexA:  i,
					NodeIndexB:  k,
					WeightValue: value,
				}
				edges = append(edges, edge)
			}
		}
	}

	// 二、从所有边去除组成最小生成树的边
	// 1. 找到算法的结束条件
	for edgeCount < cmap.Capacity-1 {
		// 2. 从边集合找出最小边
		minEdgeIndex := getMinEdge(edges)
		edges[minEdgeIndex].IsVisited = true

		// 3. 找出最小边连接的点
		nodeAIndex := edges[minEdgeIndex].NodeIndexA
		nodeBIndex := edges[minEdgeIndex].NodeIndexB

		nodeAIsInSet := false
		nodeBIsInSet := false

		nodeAInSetLabel := -1
		nodeBInSetLabel := -1

		// 4. 找出A点所在的集合
		for i := 0; i < len(nodeSets); i++ {
			nodeAIsInSet = isInSet(nodeSets[i], nodeAIndex)
			if nodeAIsInSet {
				nodeAInSetLabel = i
			}
		}

		// 找出B点所在的集合
		for i := 0; i < len(nodeSets); i++ {
			nodeBIsInSet = isInSet(nodeSets[i], nodeBIndex)
			if nodeBIsInSet {
				nodeBInSetLabel = i
			}
		}

		// 5. 根据点所在集合的不同做出不同的处理
		if nodeAInSetLabel == -1 && nodeBInSetLabel == -1 {
			var newEdges []int
			newEdges = append(newEdges, nodeAIndex, nodeBIndex)
			nodeSets = append(nodeSets, newEdges)
		} else if nodeAInSetLabel == -1 && nodeBInSetLabel != -1 {
			nodeSets[nodeBInSetLabel] = append(nodeSets[nodeBInSetLabel], nodeAIndex)
		} else if nodeAInSetLabel != -1 && nodeBInSetLabel == -1 {
			nodeSets[nodeAInSetLabel] = append(nodeSets[nodeAInSetLabel], nodeBIndex)
		} else if nodeAInSetLabel != -1 && nodeBInSetLabel != -1 {
			mergeNodeSet(&nodeSets[nodeAInSetLabel], nodeSets[nodeBInSetLabel])
			nodeSets = append(nodeSets[:nodeBInSetLabel], nodeSets[nodeBInSetLabel+1:]...)
		} else if nodeAInSetLabel != -1 && nodeBInSetLabel != -1 && nodeAInSetLabel == nodeBInSetLabel {
			continue
		}

		cmap.Edge[edgeCount] = edges[minEdgeIndex]
		edgeCount++

		fmt.Print(edges[minEdgeIndex].NodeIndexA, "--", edges[minEdgeIndex].NodeIndexB, "  ")
		fmt.Println(string(edges[minEdgeIndex].WeightValue))
	}

}

// 获取最小的边索引
func getMinEdge(edges []Edge) int {
	minWeight := 0
	edgeIndex := 0
	i := 0

	for ; i < len(edges); i++ {
		if !edges[i].IsVisited {
			minWeight = edges[i].WeightValue
			edgeIndex = i
			break
		}
	}

	if minWeight == 0 {
		return -1
	}

	for ; i < len(edges); i++ {
		if !edges[i].IsVisited {
			if edges[i].WeightValue < minWeight {
				minWeight = edges[i].WeightValue
				edgeIndex = i
			}
		}
	}

	return edgeIndex
}

// 是否在集合中
func isInSet(sets []int, target int) bool {
	for i := 0; i < len(sets); i++ {
		if sets[i] == target {
			return true
		}
	}

	return false
}

// 合并node
func mergeNodeSet(nodeSetA *[]int, nodeSetB []int) {
	*nodeSetA = append(*nodeSetA, nodeSetB...)
}

graph_test.go

package maps

import "testing"

func TestCMap(t *testing.T) {
	cmap := CreateCMap(8)

	nodeA := &Node{Data: 'A'}
	nodeB := &Node{Data: 'B'}
	nodeC := &Node{Data: 'C'}
	nodeD := &Node{Data: 'D'}
	nodeE := &Node{Data: 'E'}
	nodeF := &Node{Data: 'F'}

	cmap.AddNode(nodeA)
	cmap.AddNode(nodeB)
	cmap.AddNode(nodeC)
	cmap.AddNode(nodeD)
	cmap.AddNode(nodeE)
	cmap.AddNode(nodeF)

	cmap.SetValueToMatrixForUnDirectedGraph(0, 1, 6)
	cmap.SetValueToMatrixForUnDirectedGraph(0, 4, 5)
	cmap.SetValueToMatrixForUnDirectedGraph(0, 5, 1)
	cmap.SetValueToMatrixForUnDirectedGraph(1, 2, 3)
	cmap.SetValueToMatrixForUnDirectedGraph(1, 5, 2)
	cmap.SetValueToMatrixForUnDirectedGraph(2, 5, 8)
	cmap.SetValueToMatrixForUnDirectedGraph(2, 3, 7)
	cmap.SetValueToMatrixForUnDirectedGraph(3, 5, 4)

	// cmap.PrinMatrix()
	// cmap.DepthFirstTraverse(0)
	// cmap.BreadthFirstTraverse(0)
	// cmap.KruskalTree()
	cmap.PrimTree(0)
}
展开阅读全文
Go
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部