Skip to content

Commit 40eb135

Browse files
authored
Merge pull request #5 from yourbasic/tip
Tip
2 parents 4e527a4 + eb25885 commit 40eb135

File tree

7 files changed

+154
-110
lines changed

7 files changed

+154
-110
lines changed

README.md

+33-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Your basic graph
1+
# Your basic graph [![GoDoc](https://godoc.org/github.com/yourbasic/graph?status.svg)][godoc-graph]
22

33
### Golang library of basic graph algorithms
44

@@ -28,6 +28,7 @@ directed edges (v, w) and (w, v), both of cost c.
2828
A self-loop, an edge connecting a vertex to itself,
2929
is both directed and undirected.
3030

31+
3132
### Graph data structures
3233

3334
The type `Mutable` represents a directed graph with a fixed number
@@ -42,26 +43,56 @@ with its adjacent vertices. This makes for fast and predictable
4243
iteration: the Visit method produces its elements by reading
4344
from a fixed sorted precomputed list.
4445

46+
4547
### Virtual graphs
4648

47-
The subpackage `graph/build` offers a tool for building virtual graphs.
49+
The subpackage `graph/build` offers a tool for building graphs of type `Virtual`.
50+
4851
In a virtual graph no vertices or edges are stored in memory,
4952
they are instead computed as needed. New virtual graphs are constructed
5053
by composing and filtering a set of standard graphs, or by writing
5154
functions that describe the edges of a graph.
5255

56+
The following standard graphs are predefined:
57+
58+
- empty graphs,
59+
- complete graphs and complete bipartite graphs,
60+
- grid graphs and complete *k*-ary trees,
61+
- cycle graphs and circulant graphs,
62+
- and hypergraphs.
63+
64+
The following operations are supported:
65+
66+
- adding and deleting sets of edges,
67+
- adding cost functions,
68+
- filtering graphs by edge functions,
69+
- complement, intersection and union,
70+
- subgraphs,
71+
- connecting graphs at a single vertex,
72+
- joining two graphs by a set of edges,
73+
- matching two graphs by a set of edges,
74+
- cartesian product and tensor product.
75+
76+
Non-virtual graphs can be imported, and used as building blocks,
77+
by the `Specific` function. Virtual graphs don't need to be “exported‬”;
78+
they implement the `Iterator` interface and hence can be used directly
79+
by any algorithm in the graph package.
80+
81+
5382
### Installation
5483

5584
Once you have [installed Go][golang-install], run this command
5685
to install the `graph` package:
5786

5887
go get github.com/yourbasic/graph
88+
5989

6090
### Documentation
6191

6292
There is an online reference for the package at
6393
[godoc.org/github.com/yourbasic/graph][godoc-graph].
6494

95+
6596
### Roadmap
6697

6798
* The API of this library is frozen.

build/cartesian.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import "strconv"
66
// a graph whose vertices correspond to ordered pairs (v1, v2),
77
// where v1 and v2 are vertices in g1 and g2, respectively.
88
// The vertices (v1, v2) and (w1, w2) are connected by an edge if
9-
// v1 = w1 and (v2, w2) ∊ g2 or v2 = w2 and (v1, w1) ∊ g1.
9+
// v1 = w1 and {v2, w2} ∊ g2 or v2 = w2 and {v1, w1} ∊ g1.
1010
//
1111
// In the new graph, vertex (v1, v2) gets index n⋅v1 + v2, where n = g2.Order(),
1212
// and index i corresponds to the vertice (i/n, i%n).

build/cycle.go

+2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ func Cycle(n int) *Virtual {
2222
return
2323
})
2424

25+
// Precondition : n ≥ 3.
2526
g.degree = func(v int) int { return 2 }
2627

28+
// Precondition : n ≥ 3.
2729
g.visit = func(v int, a int, do func(w int, c int64) bool) (aborted bool) {
2830
var w [2]int
2931
switch v {

build/tensor.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import "strconv"
66
// a graph whose vertices correspond to ordered pairs (v1, v2),
77
// where v1 and v2 are vertices in g1 and g2, respectively.
88
// The vertices (v1, v2) and (w1, w2) are connected by an edge whenever
9-
// both of the edges (v1, w1) and (v2, w2) exist in the original graphs.
9+
// both of the edges {v1, w1} and {v2, w2} exist in the original graphs.
1010
//
1111
// In the new graph, vertex (v1, v2) gets index n⋅v1 + v2, where n = g2.Order(),
1212
// and index i corresponds to the vertice (i/n, i%n).

heap.go

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package graph
2+
3+
type prioQueue struct {
4+
heap []int // vertices in heap order
5+
index []int // index of each vertex in the heap
6+
cost []int64
7+
}
8+
9+
func emptyPrioQueue(cost []int64) *prioQueue {
10+
return &prioQueue{
11+
index: make([]int, len(cost)),
12+
cost: cost,
13+
}
14+
}
15+
16+
func newPrioQueue(cost []int64) *prioQueue {
17+
n := len(cost)
18+
q := &prioQueue{
19+
heap: make([]int, n),
20+
index: make([]int, n),
21+
cost: cost,
22+
}
23+
for i := range q.heap {
24+
q.heap[i] = i
25+
q.index[i] = i
26+
}
27+
return q
28+
}
29+
30+
// Len returns the number of elements in the queue.
31+
func (q *prioQueue) Len() int {
32+
return len(q.heap)
33+
}
34+
35+
// Push pushes v onto the queue.
36+
// The time complexity is O(log n) where n = q.Len().
37+
func (q *prioQueue) Push(v int) {
38+
n := q.Len()
39+
q.heap = append(q.heap, v)
40+
q.index[v] = n
41+
q.up(n)
42+
}
43+
44+
// Pop removes the minimum element from the queue and returns it.
45+
// The time complexity is O(log n) where n = q.Len().
46+
func (q *prioQueue) Pop() int {
47+
n := q.Len() - 1
48+
q.swap(0, n)
49+
q.down(0, n)
50+
51+
v := q.heap[n]
52+
q.index[v] = -1
53+
q.heap = q.heap[:n]
54+
return v
55+
}
56+
57+
// Contains tells whether v is in the queue.
58+
func (q *prioQueue) Contains(v int) bool {
59+
return q.index[v] >= 0
60+
}
61+
62+
// Fix re-establishes the ordering after the cost for v has changed.
63+
// The time complexity is O(log n) where n = q.Len().
64+
func (q *prioQueue) Fix(v int) {
65+
if i := q.index[v]; !q.down(i, q.Len()) {
66+
q.up(i)
67+
}
68+
}
69+
70+
func (q *prioQueue) less(i, j int) bool {
71+
return q.cost[q.heap[i]] < q.cost[q.heap[j]]
72+
}
73+
74+
func (q *prioQueue) swap(i, j int) {
75+
q.heap[i], q.heap[j] = q.heap[j], q.heap[i]
76+
q.index[q.heap[i]] = i
77+
q.index[q.heap[j]] = j
78+
}
79+
80+
func (q *prioQueue) up(j int) {
81+
for {
82+
i := (j - 1) / 2 // parent
83+
if i == j || !q.less(j, i) {
84+
break
85+
}
86+
q.swap(i, j)
87+
j = i
88+
}
89+
}
90+
91+
func (q *prioQueue) down(i0, n int) bool {
92+
i := i0
93+
for {
94+
j1 := 2*i + 1
95+
if j1 >= n || j1 < 0 { // j1 < 0 after int overflow
96+
break
97+
}
98+
j := j1 // left child
99+
if j2 := j1 + 1; j2 < n && q.less(j2, j1) {
100+
j = j2 // = 2*i + 2 // right child
101+
}
102+
if !q.less(j, i) {
103+
break
104+
}
105+
q.swap(i, j)
106+
i = j
107+
}
108+
return i > i0
109+
}

mst.go

+3-57
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
package graph
22

3-
import (
4-
"container/heap"
5-
)
6-
73
// MST computes a minimum spanning tree for each connected component
84
// of an undirected weighted graph.
95
// The forest of spanning trees is returned as a slice of parent pointers:
@@ -22,67 +18,17 @@ func MST(g Iterator) (parent []int) {
2218
}
2319

2420
// Prim's algorithm
25-
Q := newMstQueue(cost)
21+
Q := newPrioQueue(cost)
2622
for Q.Len() > 0 {
27-
v := heap.Pop(Q).(int)
23+
v := Q.Pop()
2824
g.Visit(v, func(w int, c int64) (skip bool) {
2925
if Q.Contains(w) && c < cost[w] {
3026
cost[w] = c
31-
Q.Update(w)
27+
Q.Fix(w)
3228
parent[w] = v
3329
}
3430
return
3531
})
3632
}
3733
return
3834
}
39-
40-
type mstQueue struct {
41-
heap []int // vertices in heap order
42-
index []int // index of each vertex in the heap
43-
cost []int64
44-
}
45-
46-
func newMstQueue(cost []int64) *mstQueue {
47-
n := len(cost)
48-
h := &mstQueue{
49-
heap: make([]int, n),
50-
index: make([]int, n),
51-
cost: cost,
52-
}
53-
for i := range h.heap {
54-
h.heap[i] = i
55-
h.index[i] = i
56-
}
57-
return h
58-
}
59-
60-
func (m *mstQueue) Len() int { return len(m.heap) }
61-
62-
func (m *mstQueue) Less(i, j int) bool {
63-
return m.cost[m.heap[i]] < m.cost[m.heap[j]]
64-
}
65-
66-
func (m *mstQueue) Swap(i, j int) {
67-
m.heap[i], m.heap[j] = m.heap[j], m.heap[i]
68-
m.index[m.heap[i]] = i
69-
m.index[m.heap[j]] = j
70-
}
71-
72-
func (m *mstQueue) Push(x interface{}) {} // Not used
73-
74-
func (m *mstQueue) Pop() interface{} {
75-
n := len(m.heap) - 1
76-
v := m.heap[n]
77-
m.index[v] = -1
78-
m.heap = m.heap[:n]
79-
return v
80-
}
81-
82-
func (m *mstQueue) Update(v int) {
83-
heap.Fix(m, m.index[v])
84-
}
85-
86-
func (m *mstQueue) Contains(v int) bool {
87-
return m.index[v] >= 0
88-
}

path.go

+5-49
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
package graph
22

3-
import (
4-
"container/heap"
5-
)
6-
73
// ShortestPath computes a shortest path from v to w.
84
// Only edges with non-negative costs are included.
95
// The number dist is the length of the path, or -1 if w cannot be reached.
@@ -44,10 +40,10 @@ func ShortestPaths(g Iterator, v int) (parent []int, dist []int64) {
4440
dist[v] = 0
4541

4642
// Dijkstra's algorithm
47-
Q := emptySpQueue(dist)
48-
heap.Push(Q, v)
43+
Q := emptyPrioQueue(dist)
44+
Q.Push(v)
4945
for Q.Len() > 0 {
50-
v = heap.Pop(Q).(int)
46+
v := Q.Pop()
5147
g.Visit(v, func(w int, d int64) (skip bool) {
5248
if d < 0 {
5349
return
@@ -56,53 +52,13 @@ func ShortestPaths(g Iterator, v int) (parent []int, dist []int64) {
5652
switch {
5753
case dist[w] == -1:
5854
dist[w], parent[w] = alt, v
59-
heap.Push(Q, w)
55+
Q.Push(w)
6056
case alt < dist[w]:
6157
dist[w], parent[w] = alt, v
62-
Q.Update(w)
58+
Q.Fix(w)
6359
}
6460
return
6561
})
6662
}
6763
return
6864
}
69-
70-
type spQueue struct {
71-
heap []int // vertices in heap order
72-
index []int // index of each vertex in the heap
73-
dist []int64
74-
}
75-
76-
func emptySpQueue(dist []int64) *spQueue {
77-
return &spQueue{dist: dist, index: make([]int, len(dist))}
78-
}
79-
80-
func (pq *spQueue) Len() int { return len(pq.heap) }
81-
82-
func (pq *spQueue) Less(i, j int) bool {
83-
return pq.dist[pq.heap[i]] < pq.dist[pq.heap[j]]
84-
}
85-
86-
func (pq *spQueue) Swap(i, j int) {
87-
pq.heap[i], pq.heap[j] = pq.heap[j], pq.heap[i]
88-
pq.index[pq.heap[i]] = i
89-
pq.index[pq.heap[j]] = j
90-
}
91-
92-
func (pq *spQueue) Push(x interface{}) {
93-
n := len(pq.heap)
94-
v := x.(int)
95-
pq.heap = append(pq.heap, v)
96-
pq.index[v] = n
97-
}
98-
99-
func (pq *spQueue) Pop() interface{} {
100-
n := len(pq.heap) - 1
101-
v := pq.heap[n]
102-
pq.heap = pq.heap[:n]
103-
return v
104-
}
105-
106-
func (pq *spQueue) Update(v int) {
107-
heap.Fix(pq, pq.index[v])
108-
}

0 commit comments

Comments
 (0)