Skip to content

Commit

Permalink
add stack details (#172)
Browse files Browse the repository at this point in the history
  • Loading branch information
spring1843 authored Feb 3, 2025
1 parent 7eef611 commit 04424ce
Show file tree
Hide file tree
Showing 9 changed files with 49 additions and 8 deletions.
2 changes: 1 addition & 1 deletion array/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func main() {

Accessing an element within an array using an index has O(1) time complexity. This means that regardless of the size of the array, read and write operations for a given element can be performed in constant time.

While arrays are useful for certain tasks, searching an unsorted array can be a time-consuming O(n) operation. Since the target item could be located anywhere in the array, every element must be checked until the item is found. Due to this limitation, alternative data structures such as trees and hash tables are often more suitable for search operations.
While arrays are useful for certain tasks, searching an unsorted array can be a time-consuming O(n) operation. Since the target item could be located anywhere in the array, every element must be checked until the item is found. Due to this limitation, alternative data structures such as [linked-lists](../linkedlist/), [trees](../tree/) and [hash tables](../hashtable/) are often more suitable for search operations.

Addition and deletion operations are O(n) operations in Arrays. Removing an element can create an empty slot that must be eliminated by shifting the remaining items. Similarly, adding items to an array may require shifting existing items to create space for the added item. These inefficiencies can make alternative data structures, such as [trees](../tree) or [hash tables](../hashtable), more suitable for managing operations involving additions and deletions.

Expand Down
2 changes: 1 addition & 1 deletion linkedlist/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Linked List

Linked lists are a collection of nodes, each capable of storing at least one data element and linked to the next node via a reference. One of the key advantages of linked lists over [arrays](../array) is their dynamic size, which allows for items to be added or removed without necessitating the resizing or shifting of other elements.
Linked lists are a collection of nodes, each capable of storing at least one data element and linked to the next node via a reference. One of the key advantages of linked lists over [arrays](../array) has traditionally been seen their dynamic size, which allows for items to be added or removed without necessitating the resizing or shifting of other elements. As shown in [queues](../queue/) with benchmarks this is different in Go due to clever slice sizing techniques used. In practice you can use slices in Go most of the time without a significant performance penalty.

Two types of linked lists exist: singly linked lists, in which each node is linked only to the next node, and doubly linked lists, in which each node is connected to both the next and previous nodes.

Expand Down
22 changes: 20 additions & 2 deletions stack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ Stacks are data structures that operate on the Last-In-First-Out (LIFO) principl

One way to visualize stacks is to think of a backpack where items are placed and later removed in reverse order, with the last item added to the bag being the first item removed.

The following diagram shows the state of a stack of size 5 when numbers 1 to 4 are pushed to the stack and 4 numbers are popped from the stack. The outcome is reversion of the inserted numbers.

```ASCII
[Figure 1] Push 1,2,3,4 to a stack and then pop 4 times.
┌───┐┌───┐┌───┐┌───┐┌───┐ ┌───┐┌───┐┌───┐┌───┐
│ ││ ││ ││ ││ │ │ ││ ││ ││ │
├───┤├───┤├───┤├───┤├───┤ ├───┤├───┤├───┤├───┤
│ ││ ││ ││ ││ 4 │ │ ││ ││ ││ │
├───┤├───┤├───┤├───┤├───┤ ├───┤├───┤├───┤├───┤
│ ││ ││ ││ 3 ││ 3 │ │ 3 ││ ││ ││ │
├───┤├───┤├───┤├───┤├───┤ ├───┤├───┤├───┤├───┤
│ ││ ││ 2 ││ 2 ││ 2 │ │ 2 ││ 2 ││ ││ │
├───┤├───┤├───┤├───┤├───┤ ├───┤├───┤├───┤├───┤
│ ││ 1 ││ 1 ││ 1 ││ 1 │ │ 1 ││ 1 ││ 1 ││ │
└───┘└───┘└───┘└───┘└───┘ └───┘└───┘└───┘└───┘
```

## Implementation

In Go, stacks can be implemented using doubly [linked lists](../linkedlist/) or [arrays and slices](../array/). Here is a linked list implementation:
Expand Down Expand Up @@ -75,13 +93,13 @@ func pop() (int, error) {

Push and pop operations in stacks are considered O(1) operations, making them highly efficient. Additionally, many machines have built-in stack instruction sets, further increasing their performance. Stacks' unique efficiency and usefulness have solidified their place as one of the most fundamental data structures, second only to [arrays](../array).

Resizing the slice and item shifting maybe necessary in the slice implementation, hence traditionally this implementation is seen as O(n). As shown in the complexity of [queue](../queue/README.md) because of the intelligent ways Go resizes the slices this is not a problem and the slice implementation of both stack and queue will perform better than the linked list implementation.
Resizing the slice and item shifting maybe necessary in the slice implementation, hence traditionally this implementation is seen as O(n). As shown in the complexity of [queue](../queue/README.md) because of the intelligent ways Go resizes the slices this is not a problem and the slice implementation of both stack and queue in Go will perform better than the linked list implementation.

## Application

Stacks are helpful when LIFO operations are desired. Many [graph](../graph) problems are solved with stacks.

During process execution, a portion of memory known as the "stack" is reserved to hold stack frames. Whenever a function is called, relevant data such as parameters, local variables, and return values are stored within a frame to be accessed after the function has been completed. When an excessive number of function calls or an infinite recursive function are made, the computer's ability to store all of this information is exceeded. This results in the well-known stack overflow error.
During process execution in operating systems, memory is divided into "stack" and "heap". The stack portion of the memory is used whenever a function is called. Relevant data such as parameters, local variables, and return values are stored within a frame in a stack to be popped after the function has been completed. When an excessive number of function calls or an infinite recursive function are made, the computer's ability to store all of this information is exceeded. This results in the well-known stack overflow error.

## Rehearsal

Expand Down
2 changes: 2 additions & 0 deletions stack/balancing_symbols_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ TestIsExpressionBalanced tests solution(s) with the following signature and prob
func IsExpressionBalanced(s string) bool
Given a set of symbols including []{}(), determine if the input is is balanced or not.
For example {{(}} is not balanced because there is no closing `)` while {{()}} is balanced.
*/
func TestIsExpressionBalanced(t *testing.T) {
tests := []struct {
Expand Down
5 changes: 4 additions & 1 deletion stack/basic_calculator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import "testing"

/*
TestBasicCalculator tests solution(s) with the following signature and problem description:
func BasicCalculator(input string) (float64, error)
Given an expression containing integers, parentheses and the four basic arithmetic operations
like 1*2+3+4*5 calculate the expression into a numerical value like 25.
{*,/,+,-} starting from the highest priority to lowest.
For example given 1*2+3+4*5 return 25 because (4*5) + (1*2) + 3 = 25.
*/
func TestBasicCalculator(t *testing.T) {
tests := []struct {
Expand Down
11 changes: 10 additions & 1 deletion stack/evaluate_postfix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@ TestEvaluatePostfix tests solution(s) with the following signature and problem d
func EvaluatePostfixExpression(expression []string) (float64, error)
Given a postfix expression like 1 2 3 + *, calculate the expression e.g. 5.
Given a postfix expression calculate its the value.
The postfix expression is a list of strings where each string is either an operator like
arithmetic symbols or an operand that are numbers.
To evaluate the postfix expression, we start scanning the expression from left to right.
If the current element is an operator we apply the operand to the last two operands we read
we then remove the operator and replace the two operands with the results of the operation.
For example given 1 2 3 + *, return 5 because 1 * (2 + 3) = 5.
*/
func TestEvaluatePostfix(t *testing.T) {
tests := []struct {
Expand Down
9 changes: 7 additions & 2 deletions stack/infix_to_postfix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ TestInfixToPostfix tests solution(s) with the following signature and problem de
func InfixToPostfix(infix []string) []string
Given an infix expression e.g. 1*2+3+4*5, convert it to a postfix expression
like 1 2 * 3 + 4 5 * supporting the four basic arithmetic operations and parentheses.
Given an infix expression convert it to a postfix expression supporting the four basic arithmetic
operations and parentheses.
Infix expression is how humans typically write arithmetic expressions like 1*2+3+4*5 which
is equivalent of (1*2) + 3 + (4*5).
For example given 1*2+3+4*5, return 1 2 * 3 + 4 5 * which both evaluate to 25.
*/
func TestInfixToPostfix(t *testing.T) {
tests := []struct {
Expand Down
2 changes: 2 additions & 0 deletions stack/longest_valid_parentheses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ TestLongestValidParentheses tests solution(s) with the following signature and p
Given a string containing parentheses, find the length of the longest valid (well-formed)
parentheses substring.
For example given "(()", return 2 because the longest valid parentheses substring is "()".
*/
func TestLongestValidParentheses(t *testing.T) {
tests := []struct {
Expand Down
2 changes: 2 additions & 0 deletions stack/max_stack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ TestMaxStack tests solution(s) with the following signature and problem descript
func (maxStack *MaxStack) Max() int
Implement a stack that can return the max of element it contains.
For example if we push {2,4,5} to the stack, max should return 5.
*/
func TestMaxStack(t *testing.T) {
tests := []struct {
Expand Down

0 comments on commit 04424ce

Please sign in to comment.