-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Enhancements for stack implementation using slice #544
Closed
+445
−350
Closed
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
d0201e1
Enhancements for stack implementation using slice
BulkBeing 5bc52c1
Revert data structure name change
BulkBeing bf45e5d
spelling mistake
BulkBeing 938f2f4
return error when stack is empty
BulkBeing 75e80a9
rename StackArray to Array
BulkBeing 92c0b54
Expose an interface for the stack package
BulkBeing 3268f14
Remove unnecessary comments
BulkBeing File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,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 | ||
} |
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,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()) | ||
} | ||
} |
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,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 | ||
} |
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,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") | ||
} | ||
}) | ||
} |
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,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 | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use our internal data structures when creating new ones. Using
container
package hides a lot of material that could be used for learning.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code was already present (I just renamed the file). Since there is another implementation using linked list, shall I remove this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keep it there, but create a new Issue to fix this 🙏🏼