go 有向图遍历及回路判断

本文探讨了如何使用Go语言进行有向图的深度优先和广度优先遍历,并介绍了利用这两种方法来判断图中是否存在回路。深度优先遍历和拓扑排序是判断回路的主要技术,通过实际的测试数据进行了验证。

有向图遍历

深度优先遍历

func depthFirst(m map[string][]string) []string {
    var order []string
    seen := make(map[string]bool)
    var visitAll func(items []string)
    visitAll = func(items []string) {
        for _, item := range items {
            if !seen[item] {
                seen[item] = true
                visitAll(m[item])
                order = append(order, item)
            }
        }
    }
    var keys []string
    for key := range m {
        keys = append(keys, key)
    }
    sort.Strings(keys)
    visitAll(keys)
    return order
}

广度优先遍历(目前遍历方向不对)

func breadthFirst(m map[string][]string) []string {
    var order []string
    seen := make(map[string]bool)
    var visitAll func(worklist []string)
    visitAll = func(worklist []string) {
        for len(worklist) > 0 {
        	items := worklist
        	worklist = nil
	        for _, item := range items {
	            if !seen[item] {
	                seen[item] = true
	                order = append(order, item)
	                worklist = append(worklist, m[item]...)
	            }
	        }
   		}
    }
    var keys []string
    for key := range m {
        keys = append(keys, key)
    }
    
    visitAll(keys)
    return order
}

测试数据

func main() {
	results := breadthFirst(prereqs)
	for i:=0; i<len(results); i++ {
		fmt.Printf("%d:\t%s\n", i+1, results[i])
	}
}

var prereqs = map[string][]string{
	"algorithms": {"data structures"},
	"calculus":   {"linear algebra"},
	"compilers": {
		"data structures",
		"formal languages",
		"computer organization",
	},

	"data structures":       {"discrete math"},
	"databases":             {"data structures"},
	"discrete math":         {"intro to programming"},
	"formal languages":      {"discrete math"},
	"networks":              {"operating systems"},
	"operating systems":     {"data structures", "computer organization"},
	"programming languages": {"data structures", "computer organization"},
}

判断回路

判断回路主要有两种方法:一种是深度优先遍历的方法,一种是拓扑排序的方法

深度优先遍历

func topoSort(m map[string][]string) []string {
	var order []string
	seen := make(map[string]int) // 初始值默认为0, 与下面notvisit状态相对应
	const (
	    notvisit = iota
		visiting  //正在访问
		visited   // 访问结束
	)
	hasRing := false
	var visitAll func(items []string)

	visitAll = func(items []string) {
		if hasRing {
			return 
		}
		for _, item := range items {
			if seen[item] == notvisit{
				seen[item] = visiting 
				visitAll(m[item])
				order = append(order, item)
				seen[item] = visited
			}else if seen[item] == visiting {
				hasRing = true
				return 
			}
		}
	}

	var keys []string
	for key := range m {
		keys = append(keys, key)
	}

	visitAll(keys) 
	if hasRing {
		fmt.Printf("found ring\n");
		order := []string
	}
	return order
}

拓扑排序

func IsContain(items []string, item string) bool {
	for _, eachItem := range items {
		if eachItem == item {
			return true
		}
	}
	return false
}


func topoSort(m map[string][]string) []string {
	var order []string
	seen := make(map[string]bool)
	hasRing := false
	count := 0 // record the visted node
	var count_last int // compare to find the ring
	
	// collect all the items
	keysMap := make(map[string]bool)
	for k, v := range m {
	    keysMap[k] = true
		for _, x := range v {
		    keysMap[x] = true
		}
	}
	var keys []string
	for key := range keysMap {
        keys = append(keys, key)
    }
	
	if len(keys) == 0 {
	    return order
	}
	
	// calculus the node's indegree
	indegree := make(map[string]int) 
	for _,k := range keys {
	    list, ok := m[k];
	    if ok {
	        indegree[k] = len(list)
	    } else {
	        indegree[k] = 0
	    }
	    
	}
	
	for {
	    if count < len(keys) {
	        count_last = count
	        for _, item := range keys {
	            if !seen[item] && indegree[item] == 0 {
					order = append(order, item)
					seen[item] = true
					count++
					// update the indegree
					for k, v := range m {
					    if IsContain(v, item){
	                        indegree[k]--
					    }
	                }
				}
	        }
	    } else {
			break
		}
		
		if count_last == count && count != len(keys)-1 {
			hasRing = true
			break
		}
	}
	
	if hasRing {
		fmt.Printf("found ring\n")
		order = []string{}
	}

	

	return order
}

测试数据

func main() {
	results := topoSort(prereqs)
	for i:=0; i<len(results); i++ {
		fmt.Printf("%d:\t%s\n", i+1, results[i])
	}
}

var prereqs = map[string][]string{
	"algorithms": {"data structures"},
	"calculus":   {"linear algebra"},
	"linear algebra": {"calculus"},
	"compilers": {
		"data structures",
		"formal languages",
		"computer organization",
	},

	"data structures":       {"discrete math"},
	"databases":             {"data structures"},
	"discrete math":         {"intro to programming"},
	"formal languages":      {"discrete math"},
	"networks":              {"operating systems"},
	"operating systems":     {"data structures", "computer organization"},
	"programming languages": {"data structures", "computer organization"},
}

参考文献:<<go语言圣经>>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值