diff --git a/structure/stack/array.go b/structure/stack/array.go new file mode 100644 index 000000000..9986f593f --- /dev/null +++ b/structure/stack/array.go @@ -0,0 +1,78 @@ +package stack + +import "errors" + +var ErrStackEmpty = errors.New("stack is empty") + +// Array is an implementation of stack with slice as underlying storage. +// ``` +// stack := stack.NewArray[int]() +// ``` +// Note that the type `Array` could also be implemented directly using a slice. +// ``` +// type Array[T any] []T +// ``` +// However, this exposes the underlying storage (slice) outside the package. +// A struct is used instead, so that the underlying storage is not accessible +// outside the package. +type Array[T any] struct { + store []T +} + +func NewArray[T any]() Interface[T] { + return new(Array[T]) +} + +// Push inserts a new element to the stack +// Push on a nil stack will panic +func (s *Array[T]) Push(val T) { + s.store = append(s.store, val) +} + +// Peek the last inserted element without removing it from the stack +// If the stack is empty, ErrStackEmpty error is returned +func (s *Array[T]) Peek() (T, error) { + var element T + if s.Empty() { + return element, ErrStackEmpty + } + return s.store[s.Len()-1], nil +} + +func (s *Array[T]) Len() int { + if s == nil { + return 0 + } + return len(s.store) +} + +func (s *Array[T]) Empty() bool { + return s.Len() == 0 +} + +// Pop returns last inserted element and removes it from the underlying storage +// If the stack is empty, ErrStackEmpty error is returned +func (s *Array[T]) Pop() (T, error) { + var element T + if s.Empty() { + return element, ErrStackEmpty + } + element = s.store[s.Len()-1] + s.store = s.store[:s.Len()-1] + return element, nil +} + +// Clear removes all elements. +// The allocated capacity remains the same and will be reused for subsequent push operations +func (s *Array[T]) Clear() { + if s == nil { + return + } + s.store = s.store[:0] +} + +func (s *Array[T]) ToSlice() []T { + out := make([]T, len(s.store)) + copy(out, s.store) + return out +} diff --git a/structure/stack/array_test.go b/structure/stack/array_test.go new file mode 100644 index 000000000..3e50f53a3 --- /dev/null +++ b/structure/stack/array_test.go @@ -0,0 +1,45 @@ +package stack + +import ( + "errors" + "testing" +) + +func Test_StackArray(t *testing.T) { + stack := NewArray[int]() + _, err := stack.Peek() + if !errors.Is(err, ErrStackEmpty) { + t.Errorf("Expected error ErrStackEmpty from Peek operation, got %v", err) + } + + _, err = stack.Pop() + if !errors.Is(err, ErrStackEmpty) { + t.Errorf("Expected error ErrStackEmpty from Pop operation, got %v", err) + } + + stack.Push(2) + stack.Push(3) + pop, err := stack.Pop() + if err != nil { + t.Errorf("Expected no errors in Pop operation, got %v", err) + } + if stack.Len() != 1 { + t.Errorf("Expected stack length 1, got %d", stack.Len()) + } + if pop != 3 { + t.Errorf("Expected popped element to be 3, got %d", pop) + } + + peek, err := stack.Peek() + if err != nil { + t.Errorf("Expected no errors in Peek operation, got %v", err) + } + if peek != 2 { + t.Errorf("Expected peek operation to return element 3, got %d", peek) + } + + stack.Clear() + if stack.Len() != 0 && !stack.Empty() { + t.Errorf("Expected stack to be empty after Clear. Got len=%d, empty=%t", stack.Len(), stack.Empty()) + } +} diff --git a/structure/stack/doubly_linked_list.go b/structure/stack/doubly_linked_list.go new file mode 100644 index 000000000..a1724ea71 --- /dev/null +++ b/structure/stack/doubly_linked_list.go @@ -0,0 +1,93 @@ +package stack + +import ( + "container/list" +) + +// doublyLinkedList is an implementation of stack.Interface using the doubly linked list provided by `container/list` as its underlying storage. +type doublyLinkedList[T any] struct { + stack *list.List +} + +func NewDoublyLinkedList[T any]() Interface[T] { + return &doublyLinkedList[T]{ + stack: list.New(), + } +} + +// Push add a value into our stack +func (dl *doublyLinkedList[T]) Push(val T) { + dl.stack.PushFront(val) +} + +// Peek return last inserted element(top of the stack) without removing it from the stack +// If the stack is empty, ErrStackEmpty error is returned +func (dl *doublyLinkedList[T]) Peek() (T, error) { + var result T + if dl.Empty() { + return result, ErrStackEmpty + } + + element := dl.stack.Front() + if element == nil { + return result, ErrStackEmpty + } + + result = element.Value.(T) + return result, nil +} + +// Pop is return last value that insert into our stack +// also it will remove it in our stack +func (dl *doublyLinkedList[T]) Pop() (T, error) { + var result T + if dl.Empty() { + return result, ErrStackEmpty + } + + element := dl.stack.Front() + if element == nil { + return result, ErrStackEmpty + } + + dl.stack.Remove(element) + result = element.Value.(T) + return result, nil +} + +// Length returns the number of elements in the stack +func (dl *doublyLinkedList[T]) Len() int { + if dl == nil { + return 0 + } + return dl.stack.Len() +} + +// Empty returns true if stack has no elements and false otherwise. +func (dl *doublyLinkedList[T]) Empty() bool { + if dl == nil { + return true + } + return dl.stack.Len() == 0 +} + +// Clear initializes the underlying storage with a new empty doubly linked list, thus clearing the underlying storage. +func (dl *doublyLinkedList[T]) Clear() { + if dl == nil { + return + } + dl.stack = list.New() +} + +// ToSlice returns the elements of stack as a slice +func (dl *doublyLinkedList[T]) ToSlice() []T { + var result []T + if dl == nil { + return result + } + + for e := dl.stack.Front(); e != nil; e = e.Next() { + result = append(result, e.Value.(T)) + } + return result +} diff --git a/structure/stack/doubly_linked_list_test.go b/structure/stack/doubly_linked_list_test.go new file mode 100644 index 000000000..d9846df81 --- /dev/null +++ b/structure/stack/doubly_linked_list_test.go @@ -0,0 +1,57 @@ +package stack + +import ( + "testing" +) + +// TestStackLinkedListWithList for testing Stack with Container/List Library (STL) +func TestStackLinkedListWithList(t *testing.T) { + st := NewDoublyLinkedList[int]() + + t.Run("Stack Push", func(t *testing.T) { + + st.Push(2) + st.Push(3) + + if st.Len() != 2 { + t.Errorf("Expected 2 elements in the stack, found %d", st.Len()) + } + }) + + t.Run("Stack Pop", func(t *testing.T) { + pop, _ := st.Pop() + + if pop != 3 { + t.Errorf("Expected 3 from Pop operation, got %d", pop) + } + + if st.Len() != 1 { + t.Errorf("Expected stack length to be 1 after Pop operation, got %d", st.Len()) + } + }) + + t.Run("Stack Peek", func(t *testing.T) { + st.Push(2) + st.Push(83) + peek, _ := st.Peek() + if peek != 83 { + t.Errorf("Expected value 83 from Peek operation, got %d", peek) + } + }) + + t.Run("Stack Len", func(t *testing.T) { + if st.Len() != 3 { + t.Errorf("Expected stack length to be 3, got %d", st.Len()) + } + }) + + t.Run("Stack Empty", func(t *testing.T) { + if st.Empty() { + t.Error("Stack should not be empty") + } + st.Clear() + if !st.Empty() { + t.Error("Stack is expected to be empty") + } + }) +} diff --git a/structure/stack/linked_list.go b/structure/stack/linked_list.go new file mode 100644 index 000000000..bbb0cfba6 --- /dev/null +++ b/structure/stack/linked_list.go @@ -0,0 +1,83 @@ +package stack + +type node[T any] struct { + Val T + Next *node[T] +} + +// linkedList implements stack.Interface using a singly linked list as the underlying storage +type linkedList[T any] struct { + top *node[T] + length int +} + +func NewLinkedList[T any]() Interface[T] { + return new(linkedList[T]) +} + +// Push value to the top of the stack +func (ll *linkedList[T]) Push(n T) { + newStack := new(node[T]) + + newStack.Val = n + newStack.Next = ll.top + + ll.top = newStack + ll.length++ +} + +// Pop returns last inserted element and removes it from the underlying storage +// If the stack is empty, ErrStackEmpty error is returned +func (ll *linkedList[T]) Pop() (T, error) { + var element T + if ll.Empty() { + return element, ErrStackEmpty + } + element = ll.top.Val + ll.top = ll.top.Next + ll.length-- + return element, nil +} + +// Empty returns true if stack has no elements and false otherwise. +func (ll *linkedList[T]) Empty() bool { + return ll.length == 0 +} + +// Len returns length of the stack +func (ll *linkedList[T]) Len() int { + return ll.length +} + +// Peek return last inserted element(top of the stack) without removing it from the stack +// If the stack is empty, ErrStackEmpty error is returned +func (ll *linkedList[T]) Peek() (T, error) { + var element T + if ll == nil || ll.length == 0 { + return element, ErrStackEmpty + } + return ll.top.Val, nil +} + +// ToSlice returns the elements of stack as a slice +func (ll *linkedList[T]) ToSlice() []T { + var elements []T + if ll == nil { + return elements + } + + current := ll.top + for current != nil { + elements = append(elements, current.Val) + current = current.Next + } + return elements +} + +func (ll *linkedList[T]) Clear() { + if ll == nil { + return + } + ll.top = nil + ll.length = 0 +} diff --git a/structure/stack/linked_list_test.go b/structure/stack/linked_list_test.go new file mode 100644 index 000000000..58d108b6e --- /dev/null +++ b/structure/stack/linked_list_test.go @@ -0,0 +1,51 @@ +package stack + +import "testing" + +// TestStackLinkedList for testing stack implementation using singly linked list +func TestStack_SinglyLinkedList(t *testing.T) { + st := NewLinkedList[int]() + + st.Push(1) + st.Push(2) + + t.Run("Stack Push", func(t *testing.T) { + result := st.ToSlice() + expected := []int{2, 1} + for x := range result { + if result[x] != expected[x] { + t.Errorf("Expected stack elements to be %v. Current elements: %v", expected, result) + } + } + }) + + t.Run("Stack isEmpty", func(t *testing.T) { + if st.Empty() { + t.Error("Stack shouldn't be emtpy") + } + }) + + t.Run("Stack Length", func(t *testing.T) { + if st.Len() != 2 { + t.Errorf("Expected stack length to be 2, got %d", st.Len()) + } + }) + + st.Pop() + pop, _ := st.Pop() + + t.Run("Stack Pop", func(t *testing.T) { + if pop != 1 { + t.Errorf("Expected 1 from Pop operation, got %d", pop) + } + }) + + st.Push(52) + st.Push(23) + st.Push(99) + t.Run("Stack Peek", func(t *testing.T) { + if val, _ := st.Peek(); val != 99 { + t.Errorf("Expected 99 from Peek operation, got %d", val) + } + }) +} diff --git a/structure/stack/stack.go b/structure/stack/stack.go new file mode 100644 index 000000000..ba49583f4 --- /dev/null +++ b/structure/stack/stack.go @@ -0,0 +1,38 @@ +// Stack is a linear data structure in which the push and pop operations occur only at one end of the structure, referred to as the top of the stack. +// The order in which an element added to or removed from a stack is described as last in, first out (LIFO). +// +// Stack Data Structure : https://www.geeksforgeeks.org/stack-data-structure-introduction-program/ +// Stack (abstract data type) : https://en.wikipedia.org/wiki/Stack_(abstract_data_type) +package stack + +// Interface defines the specifications for a stack implementation. +// Currently, there are 3 implmentatios of this stack.Interface in this package. +// stack.Array : Uses a slice as its underlying storage +// stack.LinkedList : Uses linked list as its underlying storage +// stack.DoublyLinkedList : Uses the doubly linked list provided by std library `container/list` as the underlying storage. +type Interface[T any] interface { + // Push value to the top of the stack + Push(T) + + // Pop returns last inserted element and removes it from the underlying storage + // If the stack is empty, ErrStackEmpty error is returned + Pop() (T, error) + + // Peek the last inserted element without removing it from the stack + // If the stack is empty, ErrStackEmpty error is returned + Peek() (T, error) + + // Len returns the number of elements in the stack + Len() int + + // Empty returns true if stack has no elements and false otherwise. + Empty() bool + + // Clear removes all elements by re-initializing the underlying storage + // If the underlying storage of the stack is an array/slice (created using `stack.NewArray[T]()`), + // the allocated capacity remains the same and will be reused for subsequent push operations + Clear() + + // ToSlice returns the elements of stack as a slice + ToSlice() []T +} diff --git a/structure/stack/stack_test.go b/structure/stack/stack_test.go deleted file mode 100644 index e5a77e70b..000000000 --- a/structure/stack/stack_test.go +++ /dev/null @@ -1,179 +0,0 @@ -// Stack Test -// description: based on `geeksforgeeks` description Stack is a linear data structure which follows a particular order in which the operations are performed. -// The order may be LIFO(Last In First Out) or FILO(First In Last Out). -// details: -// Stack Data Structure : https://www.geeksforgeeks.org/stack-data-structure-introduction-program/ -// Stack (abstract data type) : https://en.wikipedia.org/wiki/Stack_(abstract_data_type) -// author [Milad](https://github.com/miraddo) -// see stackarray.go, stacklinkedlist.go, stacklinkedlistwithlist.go - -package stack - -import ( - "container/list" - "reflect" - "testing" -) - -// TestStackLinkedList for testing Stack with LinkedList -func TestStackLinkedList(t *testing.T) { - var newStack Stack - - newStack.push(1) - newStack.push(2) - - t.Run("Stack Push", func(t *testing.T) { - result := newStack.show() - expected := []any{2, 1} - for x := range result { - if result[x] != expected[x] { - t.Errorf("Stack Push is not work, got %v but expected %v", result, expected) - } - } - }) - - t.Run("Stack isEmpty", func(t *testing.T) { - if newStack.isEmpty() { - t.Error("Stack isEmpty is returned true but expected false", newStack.isEmpty()) - } - - }) - - t.Run("Stack Length", func(t *testing.T) { - if newStack.len() != 2 { - t.Error("Stack Length should be 2 but got", newStack.len()) - } - }) - - newStack.pop() - pop := newStack.pop() - - t.Run("Stack Pop", func(t *testing.T) { - if pop != 1 { - t.Error("Stack Pop should return 1 but is returned", pop) - } - - }) - - newStack.push(52) - newStack.push(23) - newStack.push(99) - t.Run("Stack Peak", func(t *testing.T) { - if newStack.peak() != 99 { - t.Error("Stack Peak should return 99 but got ", newStack.peak()) - } - }) -} - -// TestStackArray for testing Stack with Array -func TestStackArray(t *testing.T) { - t.Run("Stack With Array", func(t *testing.T) { - - stackPush(2) - stackPush(3) - - t.Run("Stack Push", func(t *testing.T) { - if !reflect.DeepEqual([]any{3, 2}, stackArray) { - t.Errorf("Stack Push is not work we expected %v but got %v", []any{3, 2}, stackArray) - } - }) - - pop := stackPop() - - t.Run("Stack Pop", func(t *testing.T) { - - if stackLength() == 2 && pop != 3 { - t.Errorf("Stack Pop is not work we expected %v but got %v", 3, pop) - } - }) - - stackPush(2) - stackPush(83) - - t.Run("Stack Peak", func(t *testing.T) { - - if stackPeak() != 83 { - t.Errorf("Stack Peak is not work we expected %v but got %v", 83, stackPeak()) - } - }) - - t.Run("Stack Length", func(t *testing.T) { - if stackLength() != 3 { - t.Errorf("Stack Length is not work we expected %v but got %v", 3, stackLength()) - } - }) - - t.Run("Stack Empty", func(t *testing.T) { - if stackEmpty() == true { - t.Errorf("Stack Empty is not work we expected %v but got %v", false, stackEmpty()) - } - - stackPop() - stackPop() - stackPop() - - if stackEmpty() == false { - t.Errorf("Stack Empty is not work we expected %v but got %v", true, stackEmpty()) - } - }) - }) -} - -// TestStackLinkedListWithList for testing Stack with Container/List Library (STL) -func TestStackLinkedListWithList(t *testing.T) { - stackList := &SList{ - stack: list.New(), - } - - t.Run("Stack Push", func(t *testing.T) { - - stackList.Push(2) - stackList.Push(3) - - if stackList.Length() != 2 { - t.Errorf("Stack Push is not work we expected %v but got %v", 2, stackList.Length()) - } - }) - - t.Run("Stack Pop", func(t *testing.T) { - pop, _ := stackList.Pop() - - if stackList.Length() == 1 && pop != 3 { - t.Errorf("Stack Pop is not work we expected %v but got %v", 3, pop) - } - }) - - t.Run("Stack Peak", func(t *testing.T) { - - stackList.Push(2) - stackList.Push(83) - peak, _ := stackList.Peak() - if peak != 83 { - t.Errorf("Stack Peak is not work we expected %v but got %v", 83, peak) - } - }) - - t.Run("Stack Length", func(t *testing.T) { - if stackList.Length() != 3 { - t.Errorf("Stack Length is not work we expected %v but got %v", 3, stackList.Length()) - } - }) - - t.Run("Stack Empty", func(t *testing.T) { - if stackList.Empty() == true { - t.Errorf("Stack Empty is not work we expected %v but got %v", false, stackList.Empty()) - } - - d1, err := stackList.Pop() - d2, _ := stackList.Pop() - d3, _ := stackList.Pop() - - if err != nil { - t.Errorf("got an unexpected error %v, pop1: %v, pop2: %v, pop3: %v", err, d1, d2, d3) - } - - if stackList.Empty() == false { - t.Errorf("Stack Empty is not work we expected %v but got %v", true, stackList.Empty()) - } - }) -} diff --git a/structure/stack/stackarray.go b/structure/stack/stackarray.go deleted file mode 100644 index 5aba01cf4..000000000 --- a/structure/stack/stackarray.go +++ /dev/null @@ -1,39 +0,0 @@ -// Stack Array -// description: based on `geeksforgeeks` description Stack is a linear data structure which follows a particular order in which the operations are performed. -// The order may be LIFO(Last In First Out) or FILO(First In Last Out). -// details: -// Stack Data Structure : https://www.geeksforgeeks.org/stack-data-structure-introduction-program/ -// Stack (abstract data type) : https://en.wikipedia.org/wiki/Stack_(abstract_data_type) -// author [Milad](https://github.com/miraddo) -// see stacklinkedlist.go, stacklinkedlistwithlist.go, stack_test.go - -package stack - -var stackArray []any - -// stackPush push to first index of array -func stackPush(n any) { - stackArray = append([]any{n}, stackArray...) -} - -// stackLength return length of array -func stackLength() int { - return len(stackArray) -} - -// stackPeak return last input of array -func stackPeak() any { - return stackArray[0] -} - -// stackEmpty check array is empty or not -func stackEmpty() bool { - return len(stackArray) == 0 -} - -// stackPop return last input and remove it in array -func stackPop() any { - pop := stackArray[0] - stackArray = stackArray[1:] - return pop -} diff --git a/structure/stack/stacklinkedlist.go b/structure/stack/stacklinkedlist.go deleted file mode 100644 index 4c4973884..000000000 --- a/structure/stack/stacklinkedlist.go +++ /dev/null @@ -1,72 +0,0 @@ -// Stack Linked-List -// description: based on `geeksforgeeks` description Stack is a linear data structure which follows a particular order in which the operations are performed. -// The order may be LIFO(Last In First Out) or FILO(First In Last Out). -// details: -// Stack Data Structure : https://www.geeksforgeeks.org/stack-data-structure-introduction-program/ -// Stack (abstract data type) : https://en.wikipedia.org/wiki/Stack_(abstract_data_type) -// author [Milad](https://github.com/miraddo) -// see stacklinkedlistwithlist.go, stackarray.go, stack_test.go - -package stack - -// Node structure -type Node struct { - Val any - Next *Node -} - -// Stack has jost top of node and with length -type Stack struct { - top *Node - length int -} - -// push add value to last index -func (ll *Stack) push(n any) { - newStack := &Node{} // new node - - newStack.Val = n - newStack.Next = ll.top - - ll.top = newStack - ll.length++ -} - -// pop remove last item as first output -func (ll *Stack) pop() any { - result := ll.top.Val - if ll.top.Next == nil { - ll.top = nil - } else { - ll.top.Val, ll.top.Next = ll.top.Next.Val, ll.top.Next.Next - } - - ll.length-- - return result -} - -// isEmpty to check our array is empty or not -func (ll *Stack) isEmpty() bool { - return ll.length == 0 -} - -// len use to return length of our stack -func (ll *Stack) len() int { - return ll.length -} - -// peak return last input value -func (ll *Stack) peak() any { - return ll.top.Val -} - -// show all value as an interface array -func (ll *Stack) show() (in []any) { - current := ll.top - - for current != nil { - in = append(in, current.Val) - current = current.Next - } - return -} diff --git a/structure/stack/stacklinkedlistwithlist.go b/structure/stack/stacklinkedlistwithlist.go deleted file mode 100644 index ba3ce6580..000000000 --- a/structure/stack/stacklinkedlistwithlist.go +++ /dev/null @@ -1,60 +0,0 @@ -// Stack Linked-List with standard library (Container/List) -// description: based on `geeksforgeeks` description Stack is a linear data structure which follows a particular order in which the operations are performed. -// The order may be LIFO(Last In First Out) or FILO(First In Last Out). -// details: -// Stack Data Structure : https://www.geeksforgeeks.org/stack-data-structure-introduction-program/ -// Stack (abstract data type) : https://en.wikipedia.org/wiki/Stack_(abstract_data_type) -// author [Milad](https://github.com/miraddo) -// see stacklinkedlist.go, stackarray.go, stack_test.go - -package stack - -import ( - "container/list" - "fmt" -) - -// SList is our struct that point to stack with container/list.List library -type SList struct { - stack *list.List -} - -// Push add a value into our stack -func (sl *SList) Push(val any) { - sl.stack.PushFront(val) -} - -// Peak is return last value that insert into our stack -func (sl *SList) Peak() (any, error) { - if !sl.Empty() { - element := sl.stack.Front() - return element.Value, nil - } - return "", fmt.Errorf("stack list is empty") -} - -// Pop is return last value that insert into our stack -// also it will remove it in our stack -func (sl *SList) Pop() (any, error) { - if !sl.Empty() { - // get last element that insert into stack - element := sl.stack.Front() - // remove element in stack - sl.stack.Remove(element) - // return element value - return element.Value, nil - } - return "", fmt.Errorf("stack list is empty") -} - -// Length return length of our stack -func (sl *SList) Length() int { - return sl.stack.Len() -} - -// Empty check our stack has value or not -func (sl *SList) Empty() bool { - // check our stack is empty or not - // if is 0 it means our stack is empty otherwise is not empty - return sl.stack.Len() == 0 -}