diff --git a/graph/dijkstra.go b/graph/dijkstra.go new file mode 100644 index 000000000..26b2becbc --- /dev/null +++ b/graph/dijkstra.go @@ -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 +} diff --git a/graph/dijkstra_test.go b/graph/dijkstra_test.go new file mode 100644 index 000000000..168c7da69 --- /dev/null +++ b/graph/dijkstra_test.go @@ -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) + } + }) + } +} diff --git a/sort/heapsort.go b/sort/heapsort.go index 698c7610f..0c44eb515 100644 --- a/sort/heapsort.go +++ b/sort/heapsort.go @@ -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 }