-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
merge: feat: dijkstra closest distance implementation (#415)
* refactor * feat: implement dijkstra * test: adding multi path test cases * fix: varible naming in test * refactor * feat: generalize heap * feat: new implementation * cleanup * codespell * nit * fix conflict * Update graph/dijkstra.go Co-authored-by: Taj <[email protected]> * Update graph/dijkstra.go Co-authored-by: Taj <[email protected]> * (refactor) Co-authored-by: Taj <[email protected]> Co-authored-by: Rak Laptudirm <[email protected]>
- Loading branch information
1 parent
153ee2c
commit 3a02506
Showing
3 changed files
with
217 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package graph | ||
|
||
import "github.com/TheAlgorithms/Go/sort" | ||
|
||
type Item struct { | ||
node int | ||
dist int | ||
} | ||
|
||
func (a Item) More(b interface{}) bool { | ||
// reverse direction for minheap | ||
return a.dist < b.(Item).dist | ||
} | ||
func (a Item) Idx() int { | ||
return a.node | ||
} | ||
|
||
func (g *Graph) Dijkstra(start, end int) (int, bool) { | ||
visited := make(map[int]bool) | ||
nodes := make(map[int]*Item) | ||
|
||
nodes[start] = &Item{ | ||
dist: 0, | ||
node: start, | ||
} | ||
pq := sort.MaxHeap{} | ||
pq.Init(nil) | ||
pq.Push(*nodes[start]) | ||
|
||
visit := func(curr Item) { | ||
visited[curr.node] = true | ||
for n, d := range g.edges[curr.node] { | ||
if visited[n] { | ||
continue | ||
} | ||
|
||
item := nodes[n] | ||
dist2 := curr.dist + d | ||
if item == nil { | ||
nodes[n] = &Item{node: n, dist: dist2} | ||
pq.Push(*nodes[n]) | ||
} else if item.dist > dist2 { | ||
item.dist = dist2 | ||
pq.Update(*item) | ||
} | ||
} | ||
} | ||
|
||
for pq.Size() > 0 { | ||
curr := pq.Pop().(Item) | ||
if curr.node == end { | ||
break | ||
} | ||
visit(curr) | ||
} | ||
|
||
item := nodes[end] | ||
if item == nil { | ||
return -1, false | ||
} | ||
return item.dist, true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package graph | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
var tc_dijkstra = []struct { | ||
name string | ||
edges [][]int | ||
node0 int | ||
node1 int | ||
expected int | ||
}{ | ||
{ | ||
"straight line graph", | ||
[][]int{{0, 1, 5}, {1, 2, 2}}, | ||
0, 2, 7, | ||
}, | ||
{ | ||
"unconnected node", | ||
[][]int{{0, 1, 5}}, | ||
0, 2, -1, | ||
}, | ||
{ | ||
"double paths", | ||
[][]int{{0, 1, 5}, {1, 3, 5}, {0, 2, 5}, {2, 3, 4}}, | ||
0, 3, 9, | ||
}, | ||
{ | ||
"double paths extended", | ||
[][]int{{0, 1, 5}, {1, 3, 5}, {0, 2, 5}, {2, 3, 4}, {3, 4, 1}}, | ||
0, 4, 10, | ||
}, | ||
} | ||
|
||
func TestDijkstra(t *testing.T) { | ||
for _, tc := range tc_dijkstra { | ||
t.Run(tc.name, func(t *testing.T) { | ||
var graph Graph | ||
for _, edge := range tc.edges { | ||
graph.AddWeightedEdge(edge[0], edge[1], edge[2]) | ||
} | ||
|
||
actual, _ := graph.Dijkstra(tc.node0, tc.node1) | ||
if actual != tc.expected { | ||
t.Errorf("expected %d, got %d, from node %d to %d, with %v", | ||
tc.expected, actual, tc.node0, tc.node1, tc.edges) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,134 @@ | ||
package sort | ||
|
||
type maxHeap struct { | ||
slice []int | ||
type MaxHeap struct { | ||
slice []Comparable | ||
heapSize int | ||
indices map[int]int | ||
} | ||
|
||
func buildMaxHeap(slice []int) maxHeap { | ||
h := maxHeap{slice: slice, heapSize: len(slice)} | ||
for i := len(slice) / 2; i >= 0; i-- { | ||
h.MaxHeapify(i) | ||
func buildMaxHeap(slice0 []int) MaxHeap { | ||
var slice []Comparable | ||
for _, i := range slice0 { | ||
slice = append(slice, Int(i)) | ||
} | ||
h := MaxHeap{} | ||
h.Init(slice) | ||
return h | ||
} | ||
|
||
func (h maxHeap) MaxHeapify(i int) { | ||
func (h *MaxHeap) Init(slice []Comparable) { | ||
if slice == nil { | ||
slice = make([]Comparable, 0) | ||
} | ||
|
||
h.slice = slice | ||
h.heapSize = len(slice) | ||
h.indices = make(map[int]int) | ||
h.Heapify() | ||
} | ||
|
||
func (h MaxHeap) Heapify() { | ||
for i, v := range h.slice { | ||
h.indices[v.Idx()] = i | ||
} | ||
for i := h.heapSize / 2; i >= 0; i-- { | ||
h.heapifyDown(i) | ||
} | ||
} | ||
|
||
func (h *MaxHeap) Pop() Comparable { | ||
if h.heapSize == 0 { | ||
return nil | ||
} | ||
|
||
i := h.slice[0] | ||
h.heapSize-- | ||
|
||
h.slice[0] = h.slice[h.heapSize] | ||
h.updateidx(0) | ||
h.heapifyDown(0) | ||
|
||
h.slice = h.slice[0:h.heapSize] | ||
return i | ||
} | ||
|
||
func (h *MaxHeap) Push(i Comparable) { | ||
h.slice = append(h.slice, i) | ||
h.updateidx(h.heapSize) | ||
h.heapifyUp(h.heapSize) | ||
h.heapSize++ | ||
} | ||
|
||
func (h MaxHeap) Size() int { | ||
return h.heapSize | ||
} | ||
|
||
func (h MaxHeap) Update(i Comparable) { | ||
h.slice[h.indices[i.Idx()]] = i | ||
h.heapifyUp(h.indices[i.Idx()]) | ||
h.heapifyDown(h.indices[i.Idx()]) | ||
} | ||
|
||
func (h MaxHeap) updateidx(i int) { | ||
h.indices[h.slice[i].Idx()] = i | ||
} | ||
|
||
func (h MaxHeap) heapifyUp(i int) { | ||
if i == 0 { | ||
return | ||
} | ||
p := i / 2 | ||
|
||
if h.slice[i].More(h.slice[p]) { | ||
h.slice[i], h.slice[p] = h.slice[p], h.slice[i] | ||
h.updateidx(i) | ||
h.updateidx(p) | ||
h.heapifyUp(p) | ||
} | ||
} | ||
|
||
func (h MaxHeap) heapifyDown(i int) { | ||
l, r := 2*i+1, 2*i+2 | ||
max := i | ||
|
||
if l < h.size() && h.slice[l] > h.slice[max] { | ||
if l < h.heapSize && h.slice[l].More(h.slice[max]) { | ||
max = l | ||
} | ||
if r < h.size() && h.slice[r] > h.slice[max] { | ||
if r < h.heapSize && h.slice[r].More(h.slice[max]) { | ||
max = r | ||
} | ||
//log.Printf("MaxHeapify(%v): l,r=%v,%v; max=%v\t%v\n", i, l, r, max, h.slice) | ||
if max != i { | ||
h.slice[i], h.slice[max] = h.slice[max], h.slice[i] | ||
h.MaxHeapify(max) | ||
h.updateidx(i) | ||
h.updateidx(max) | ||
h.heapifyDown(max) | ||
} | ||
} | ||
|
||
func (h maxHeap) size() int { return h.heapSize } // ??? | ||
type Comparable interface { | ||
Idx() int | ||
More(interface{}) bool | ||
} | ||
type Int int | ||
|
||
func (a Int) More(b interface{}) bool { | ||
return a > b.(Int) | ||
} | ||
func (a Int) Idx() int { | ||
return int(a) | ||
} | ||
|
||
func HeapSort(slice []int) []int { | ||
h := buildMaxHeap(slice) | ||
//log.Println(slice) | ||
for i := len(h.slice) - 1; i >= 1; i-- { | ||
h.slice[0], h.slice[i] = h.slice[i], h.slice[0] | ||
h.heapSize-- | ||
h.MaxHeapify(0) | ||
/*if i == len(h.slice)-1 || i == len(h.slice)-3 || i == len(h.slice)-5 { | ||
element := (i - len(h.slice)) * -1 | ||
fmt.Println("Heap after removing ", element, " elements") | ||
fmt.Println(h.slice) | ||
h.heapifyDown(0) | ||
} | ||
|
||
}*/ | ||
res := []int{} | ||
for _, i := range h.slice { | ||
res = append(res, int(i.(Int))) | ||
} | ||
return h.slice | ||
return res | ||
} |