Skip to content

add heap, quick and insertion sorts #87

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Welcome to **Data Structures and Algorithms in Go**! 🎉 This project is design
* [Equal Sum Sub-arrays](./array/equal_sum_subarrays_test.go)
* [Rotate K Times](./array/rotate_k_steps_test.go)
* [Bubble Sort](./array/bubble_sort_test.go)
* [Insertion Sort](./array/insertion_sort_test.go), [Solution](./array/insertion_sort.go)
* [Strings](./strings/README.md)
* [The longest Dictionary Word Containing Key](./strings/longest_dictionary_word_test.go)
* [Look and Tell](./strings/look_and_tell_test.go)
Expand Down Expand Up @@ -70,6 +71,7 @@ Welcome to **Data Structures and Algorithms in Go**! 🎉 This project is design
* [Median in a Stream](./heap/median_in_a_stream_test.go)
* [Kth Closest Points to the Center](./heap/k_closest_points_to_origin_test.go)
* [Sliding Maximum](./heap/sliding_maximum_test.go)
* [Heap Sort](./heap/heap_sort_test.go), [Solution](./heap/heap_sort.go)
* Algorithms
* [Recursion](./recursion/README.md)
* [Reverse an integer recursively](./recursion/reverse_number_test.go)
Expand All @@ -83,6 +85,7 @@ Welcome to **Data Structures and Algorithms in Go**! 🎉 This project is design
* [Rate Limit](./dnc/rate_limit_test.go)
* [Towers of Hanoi](./dnc/towers_of_hanoi_test.go)
* [Merge Sort](./dnc/merge_sort_test.go)
* [Quick Sort](./dnc/quick_sort_test.go)
* [Bit Manipulation](./bit//README.md)
* [Division without multiplication or division operators](./bit/division_without_operators_test.go)
* [Middle without division](./bit/middle_without_division_test.go)
Expand Down
1 change: 1 addition & 0 deletions array/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,4 @@ Arrays are used wherever sequential data or more than one piece of data is neede
* [Equal Sum Sub-arrays](./equal_sum_subarrays_test.go), [Solution](./equal_sum_subarrays.go)
* [Rotate K Times](./rotate_k_steps_test.go), [Solution](./rotate_k_steps.go)
* [Bubble Sort](./bubble_sort_test.go), [Solution](bubble_sort.go)
* [Insertion Sort](./insertion_sort_test.go), [Solution](./insertion_sort.go)
18 changes: 18 additions & 0 deletions array/insertion_sort.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package array

// InsertionSort solves the problem in O(n^2) time and O(1) space.
func InsertionSort(list []int) []int {
for i := 1; i < len(list); i++ {
key := list[i]
j := i - 1

for j >= 0 && list[j] > key {
list[j+1] = list[j]
j--
}

list[j+1] = key
}

return list
}
35 changes: 35 additions & 0 deletions array/insertion_sort_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package array

import (
"reflect"
"testing"
)

/*
TestInsertionSort tests solution(s) with the following signature and problem description:

InsertionSort(input []int)

Given an array of unsorted integers, sort the array using the Insertion Sort algorithm.
The algorithm should be in-place, meaning it should not create a new array. The algorithm
works by dividing the input array into sorted and unsorted sections, and moving items from
the unsorted section into the sorted section, one item at a time.
*/
func TestInsertionSort(t *testing.T) {
tests := []struct {
input, sorted []int
}{
{[]int{}, []int{}},
{[]int{1}, []int{1}},
{[]int{1, 2}, []int{1, 2}},
{[]int{2, 1}, []int{1, 2}},
{[]int{2, 3, 1}, []int{1, 2, 3}},
{[]int{4, 2, 3, 1, 5}, []int{1, 2, 3, 4, 5}},
}
for i, test := range tests {
InsertionSort(test.input)
if !reflect.DeepEqual(test.input, test.sorted) {
t.Fatalf("Failed test case #%d. Want %v got %v", i, test.sorted, test.input)
}
}
}
1 change: 1 addition & 0 deletions dnc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,4 @@ DNC algorithms are suitable for solving problems that can be divided into smalle
* [Rate Limit](rate_limit_test.go), [Solution](rate_limit.go)
* [Towers of Hanoi](towers_of_hanoi_test.go), [Solution](towers_of_hanoi.go)
* [Merge Sort](merge_sort_test.go), [Solution](merge_sort.go)
* [Quick Sort](quick_sort_test.go), [Solution](quick_sort.go)
25 changes: 25 additions & 0 deletions dnc/quick_sort.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dnc

// QuickSort solves the problem in O(n*Log n) time and O(n) space.
func QuickSort(list []int) []int {
if len(list) <= 1 {
return list
}

pivot := list[len(list)/2]

var less, equal, greater []int
for i := 0; i < len(list); i++ {
if list[i] == pivot {
equal = append(equal, list[i])
}
if list[i] < pivot {
less = append(less, list[i])
}
if list[i] > pivot {
greater = append(greater, list[i])
}
}

return append(append(QuickSort(less), equal...), QuickSort(greater)...)
}
34 changes: 34 additions & 0 deletions dnc/quick_sort_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package dnc

import (
"reflect"
"testing"
)

/*
TestQuickSort tests solution(s) with the following signature and problem description:

func QuickSort(list []int) []int

Given a list of integers like {3,1,2}, return a sorted set like {1,2,3} using Quick Sort.
*/
func TestQuickSort(t *testing.T) {
tests := []struct {
list []int
sorted []int
}{
{[]int{}, []int{}},
{[]int{1, 2}, []int{1, 2}},
{[]int{2, 1}, []int{1, 2}},
{[]int{1, 2, 3}, []int{1, 2, 3}},
{[]int{3, 2, 1}, []int{1, 2, 3}},
{[]int{1, 3, 2}, []int{1, 2, 3}},
{[]int{-1, 3, 2, 0, 4}, []int{-1, 0, 2, 3, 4}},
}

for i, test := range tests {
if got := QuickSort(test.list); !reflect.DeepEqual(got, test.sorted) {
t.Fatalf("Failed test case #%d. Want %v got %v", i, test.sorted, got)
}
}
}
5 changes: 3 additions & 2 deletions heap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ A heap must satisfy two conditions:
1. The structure property requires that the heap be a complete binary search [tree](../tree), where each level is filled left to right, and all levels except the bottom are full.
2. The heap property requires that the children of a node be larger than or equal to the parent node in a min heap and smaller than or equal to the parent in a max heap, meaning that the root is the minimum in a min heap and the maximum in a max heap.

As a result, if you push elements to the min or max heap and then pop them one by one, you will obtain a list that is sorted in ascending or descending order, respectively. This sorting technique is also an O(n*Log n) algorithm known as heap sort. Although there are other sorting algorithms available, none of them are faster than O(n*Logn).
As a result, if you push elements to the min or max heap and then pop them one by one, you will obtain a list that is sorted in ascending or descending order, respectively. This sorting technique is also an O(n*Log n) algorithm known as [heap sort](./heap_sort_test.go). Although there are other sorting algorithms available, none of them are faster than O(n*Logn).

When pushing a new element to a heap, because of the structure property we always add the new element to the first available position on the lowest level of the heap, filling from left to right. Then to maintain the heap property, if the newly inserted element is smaller than its parent in a min heap (larger in a max heap), then we swap it with its parent. We continue swapping the swapped element with its parent until the heap property is achieved.
When pushing a new element to a heap, because of the structure property we always add the new element to the first available position on the lowest level of the heap, filling from left to right. Then to maintain the heap property, if the newly inserted element is smaller than its parent in a min heap (larger in a max heap), then we swap it with its parent. We continue swapping the swapped element with its parent until the heap property is achieved.

```ASCII
[Figure 1] Minimum heap push operation
Expand Down Expand Up @@ -100,3 +100,4 @@ Priority queues implemented as heaps are utilized in job scheduling, for example
* [Median in a Stream](median_in_a_stream_test.go), [Solution](median_in_a_stream_test.go)
* [Kth Closest Points to the Center](k_closest_points_to_origin_test.go), [Solution](k_closest_points_to_origin.go)
* [Sliding Maximum](sliding_maximum_test.go), [Solution](sliding_maximum.go)
* [Heap Sort](heap_sort_test.go), [Solution](heap_search.go)
108 changes: 108 additions & 0 deletions heap/heap_sort.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package heap

import "math"

type (
// Vertex is a node in a heap.
Vertex struct {
// Val is the value of the vertex.
Val int
// Left is the left child of the vertex.
Left *Vertex
// Right is the right child of the vertex.
Right *Vertex
}
// MinHeap is a heap where the root is always the minimum value.
MinHeap struct {
Data []*Vertex
}
)

// HeapSort solves the problem in O(n*Log n) time and O(n) space.
func HeapSort(list []int) []int {
sorted := []int{}
heap := NewMinHeap()
for _, val := range list {
heap.Push(val)
}
for heap.Len() > 0 {
sorted = append(sorted, heap.Pop())
}
return sorted
}

// Returns a new Min Heap.
func NewMinHeap() *MinHeap {
return &MinHeap{
Data: []*Vertex{},
}
}

// Push inserts a new value into the heap.
func (m *MinHeap) Push(value int) {
vertex := &Vertex{
Val: value,
}
m.Data = append(m.Data, vertex)
m.heapifyUp(len(m.Data) - 1)
}

// Pop removes the root value from the heap.
func (m *MinHeap) Pop() int {
if len(m.Data) == 0 {
return math.MinInt
}

rootValue := m.Data[0].Val
lastIndex := len(m.Data) - 1
m.Data[0] = m.Data[lastIndex]
m.Data = m.Data[:lastIndex]
m.heapifyDown(0)
return rootValue
}

// Len returns the number of elements in the heap.
func (m *MinHeap) Len() int {
return len(m.Data)
}

// heapifyUp moves the vertex up the heap to maintain the heap property.
func (m *MinHeap) heapifyUp(index int) {
for index > 0 {
parentIndex := (index - 1) / 2
if m.Data[parentIndex].Val <= m.Data[index].Val {
break
}
m.swap(parentIndex, index)
index = parentIndex
}
}

// heapifyDown moves the vertex down the heap to maintain the heap property.
func (m *MinHeap) heapifyDown(index int) {
for {
leftChildIndex := 2*index + 1
rightChildIndex := 2*index + 2
smallestIndex := index

if leftChildIndex < len(m.Data) && m.Data[leftChildIndex].Val < m.Data[smallestIndex].Val {
smallestIndex = leftChildIndex
}

if rightChildIndex < len(m.Data) && m.Data[rightChildIndex].Val < m.Data[smallestIndex].Val {
smallestIndex = rightChildIndex
}

if smallestIndex == index {
break
}

m.swap(index, smallestIndex)
index = smallestIndex
}
}

// swap swaps the positions of two vertices in the heap.
func (m *MinHeap) swap(i, j int) {
m.Data[i], m.Data[j] = m.Data[j], m.Data[i]
}
46 changes: 46 additions & 0 deletions heap/heap_sort_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package heap

import (
"math"
"reflect"
"testing"
)

/*
TestHeapSort tests solution(s) with the following signature and problem description:

func HeapSort(list []int) []int

Given a list of integers like {3,1,2}, return a sorted set like {1,2,3} using Heap Sort.
*/
func TestHeapSort(t *testing.T) {
tests := []struct {
list []int
sorted []int
}{
{[]int{}, []int{}},
{[]int{1, 2}, []int{1, 2}},
{[]int{2, 1}, []int{1, 2}},
{[]int{1, 2, 3}, []int{1, 2, 3}},
{[]int{3, 2, 1}, []int{1, 2, 3}},
{[]int{1, 3, 2}, []int{1, 2, 3}},
{[]int{-1, 3, 2, 0, 4}, []int{-1, 0, 2, 3, 4}},
}

for i, test := range tests {
if got := HeapSort(test.list); !reflect.DeepEqual(got, test.sorted) {
t.Fatalf("Failed test case #%d. Want %v got %v", i, test.sorted, got)
}
}
}

func TestMinHeapImplementation(t *testing.T) {
heap := NewMinHeap()
if got := heap.Pop(); got != math.MinInt {
t.Fatalf("Failed test case. Want %v got %v", math.MinInt, got)
}
heap.Push(1)
if got := heap.Pop(); got != 1 {
t.Fatalf("Failed test case. Want %v got %v", 1, got)
}
}