Stack
A stack is a restricted linear list in which insertion and deletion are allowed only at one end, and which obeys the last in, first out (LIFO) principle. The stack underlies recursion, function calls, expression evaluation and many other algorithms, and it is a high-frequency topic in all kinds of examinations. After reading this chapter you will be able to understand the definition, the characteristics and the basic operations of a stack, master the implementation and the complexity of pushing, popping and reading the top of a sequential stack, master the implementation and the characteristics of a linked stack, master the classic applications such as bracket matching and expression evaluation, and judge whether a given pop sequence is legal.
How can the last in, first out property be used to solve problems? This chapter revolves around the definition of the stack, the two storage implementations (the sequential stack and the linked stack) and the classic applications (bracket matching, expression evaluation and so on), so that you can understand why a stack is a structure whose operations are restricted yet whose uses are extremely broad.
Basic route: basic concepts → sequential stack → linked stack; master the definition of the stack and its two implementations first.
Advanced route: classic applications → summary; see the practical value of the stack through bracket matching and expression evaluation.
Definition
A definition is a precise description of what a concept, a term, or an object means. Understanding definitions is the foundation of learning any field, and every concept has its own exact meaning.
A stack is a last in, first out (Last In First Out, abbreviated LIFO) linear list in which insertion and deletion are allowed only at one end, called the top.
- The insertion operation is called push
- The deletion operation is called pop
- A stack that contains no elements is called empty
Characteristics
- Last in, first out (LIFO): the element that was pushed last is popped first
- Restricted operations: elements can be inserted and deleted only at the top, not at arbitrary positions as in a general linear list
- Fixed bottom: the bottom of the stack never moves, while the top changes dynamically with every operation
Basic Operations
| Operation | Description |
|---|---|
InitStack(&S) | Initializes an empty stack |
StackEmpty(S) | Tests whether the stack is empty |
Push(&S, x) | Pushes the element x onto the stack |
Pop(&S, &x) | Pops the top element and returns it in x |
GetTop(S, &x) | Reads the top element and returns it in x without popping it |
Exercises
Exercise 1
The characteristic of a stack is ( )
A. First in, first out B. Last in, first out C. Random in, random out D. Operations allowed only at both ends
Approach:
This question tests the basic characteristic of a stack. To decide it, focus on the operational restriction that a stack allows insertion and deletion only at one end, the top; the popping order therefore has to be last in, first out, and the options describing other structures can be ruled out.
Steps:
- A stack is a restricted linear list in which insertion (push) and deletion (pop) are both allowed only at the top end.
- Because the element pushed last is popped first, the ordering characteristic of a stack is last in, first out (LIFO), so option B is correct.
- Option A, first in, first out, is the characteristic of a queue; option C, random in and random out, violates the operational restriction of a stack; option D, operations allowed only at both ends, is the characteristic of a deque.
Answer:
B.
Exercise 2
Suppose the input sequence of a stack is 1, 2, 3. After a series of push and pop operations, which of the following pop sequences cannot be produced? ( )
A. 3, 2, 1 B. 2, 1, 3 C. 3, 1, 2 D. 1, 2, 3
Approach:
This question asks which pop sequence is impossible. The method is to simulate the push and pop operations: for an element to be popped first, every element above it must already have been pushed, so the constraint that the top is always the element pushed last and not yet popped can be used to rule out candidates.
Steps:
- If 3 is to be popped first, then 1, 2 and 3 must all have been pushed, and the stack from bottom to top is [1, 2, 3].
- After 3 is popped the top becomes 2, so the next element popped can only be 2, never 1.
- Therefore the sequence 3, 1, 2 cannot be produced by any sequence of push and pop operations, and it is the impossible pop sequence.
Answer:
C.
A sequential stack is implemented with an array, and the top pointer top points at the top element, or at the position just after the top element.
#define MaxSize 100
typedef struct {
int data[MaxSize];
int top; // top pointer, initially -1, pointing at the top element
} SqStack;
Push
bool Push(SqStack &S, int x) {
if (S.top == MaxSize - 1) return false; // the stack is full
S.data[++S.top] = x; // increment first, then push
return true;
}
Pop
bool Pop(SqStack &S, int &x) {
if (S.top == -1) return false; // the stack is empty
x = S.data[S.top--]; // pop first, then decrement
return true;
}
Read the Top Element
bool GetTop(SqStack S, int &x) {
if (S.top == -1) return false;
x = S.data[S.top];
return true;
}
Time complexity: push, pop and reading the top element are all .
top reaches MaxSize-1 no further element can be pushed. If the size of the stack cannot be determined in advance, a linked stack is the better choice.Shared Stack
Two stacks share the same array space and grow from the two ends towards the middle, which makes full use of the space and reduces the chance of either stack becoming full.
typedef struct {
int data[MaxSize];
int top0; // top of stack 0, initially -1, grows to the right
int top1; // top of stack 1, initially MaxSize, grows to the left
} ShareStack;
// the stacks are full when: top0 + 1 == top1
A linked stack is implemented with a linked list whose top is the head of the list, that is, the node right after the dummy head node. Both push and pop work at the head, so there is no need to worry about the stack becoming full.
typedef struct SNode {
int data;
struct SNode *next;
} SNode, *LinkStack;
// push (insertion at the head)
bool Push(LinkStack &S, int x) {
SNode *s = (SNode *)malloc(sizeof(SNode));
s->data = x;
s->next = S->next; // S is the dummy head node
S->next = s;
return true;
}
// pop
bool Pop(LinkStack &S, int &x) {
if (S->next == NULL) return false; // the stack is empty
SNode *p = S->next;
x = p->data;
S->next = p->next;
free(p);
return true;
}
Time complexity: both push and pop are , and a linked stack never becomes full.
Exercises
Exercise 1
What are the advantages and the disadvantages of the sequential storage and the linked storage of a stack?
Approach:
This question tests the comparison of the two storage structures of a stack. In the answer, comment on the sequential stack and on the linked stack from four points of view: how the space is allocated, whether random access is supported, the risk of stack overflow, and the cost of insertion and deletion.
Steps:
- Sequential stack: the space is contiguous, the implementation is simple and random access is supported, but the space has to be allocated in advance, so there is a risk of stack overflow.
- Linked stack: the space is allocated on demand and the stack never becomes full, and both insertion and deletion are performed at the top, that is, at the head of the list (), but random access is not supported and every node needs an extra pointer.
- Conclusion about the trade-off: use a sequential stack when the size of the stack can be predicted and random access is required, and use a linked stack when the size varies a lot or dynamic resizing is required.
Answer:
Sequential stack: the space is contiguous, the implementation is simple and random access is supported, but the space must be allocated in advance, so there is a risk of stack overflow. Linked stack: the space is allocated on demand and the stack never becomes full, and both insertion and deletion happen at the head (), but random access is not supported and every node needs an extra pointer.
Bracket Matching
Checking whether the left and right brackets in an expression match is a typical application of the stack: push a left bracket when it is met, and pop and match it when a right bracket is met.
bool Match(char str[], int n) {
SqStack S;
InitStack(S);
for (int i = 0; i < n; i++) {
if (str[i] == '(' || str[i] == '[' || str[i] == '{') {
Push(S, str[i]); // push the left bracket
} else {
if (StackEmpty(S)) return false; // no left bracket left to match
char top;
Pop(S, top);
// check whether the two brackets match
if (str[i] == ')' && top != '(') return false;
if (str[i] == ']' && top != '[') return false;
if (str[i] == '}' && top != '{') return false;
}
}
return StackEmpty(S); // everything matches only if the stack is empty at the end
}
Expression Evaluation
Expression evaluation usually takes two steps: converting infix to postfix, and then evaluating the postfix expression.
Infix to postfix: a stack holds the operators, and the precedence rules are followed:
- An operand is output directly
- A left parenthesis is pushed onto the stack
- On meeting an operator, every operator in the stack whose precedence is not lower than it is popped, and then the new operator is pushed
- On meeting a right parenthesis, pop until the matching left parenthesis is removed
Evaluating a postfix expression: scan from left to right, pushing an operand onto the stack; when an operator is met, pop two operands, compute the result and push it back onto the stack.
For example, the infix expression (a+b)*c becomes the postfix expression ab+c*.
Other Applications
- Function calls: managing the stack frames of recursive calls
- Recursive implementation: recursion is a stack in essence
- Browser forward and back buttons: two stacks record the history separately
- Editor undo and redo: two stacks store the operation records
An everyday analogy for a stack: a pile of plates, where the plate that was placed on top last is the first one taken away. This is exactly last in, first out (LIFO).
Exercises
Exercise 1
Give examples of the typical applications of a stack.
Approach:
This question asks for examples of the typical applications of a stack. The idea is first to point out that all of these applications exploit the last in, first out property of a stack together with the fact that it operates at one end only, and then to list them one by one according to the scenario, explaining the role the stack plays in each of them.
Steps:
- Expression evaluation: both the conversion of an infix expression into a postfix expression and the evaluation of a postfix expression use a stack to hold operators and intermediate results.
- Bracket matching: the stack records the left brackets that have not been closed yet, and a right bracket is paired with the left bracket on the top of the stack.
- Function calls and recursive implementation: the stack holds the return address, the parameters and the local variables of every level, and the calling context is restored in last in, first out order when a function returns.
- Browser forward and back buttons, editor undo and redo: the stack holds the historical states, and the most recent operation is the first one to be undone.
Answer:
The typical applications of a stack include expression evaluation (converting infix to postfix and evaluating the postfix expression), bracket matching, function calls and recursive implementation, the forward and back buttons of a browser, and the undo and redo of an editor.
Exercise 2
How can a stack be used to check whether the brackets in a string match? Briefly describe the algorithm.
Approach:
This question tests the use of a stack to check bracket matching. The idea is to let the stack hold the left brackets that have been met but not yet closed, to pair a right bracket with the left bracket on the top of the stack, and finally to check whether the stack is empty in order to decide whether everything matches.
Steps:
- Initialize an empty stack.
- Scan the string from left to right: whenever a left bracket (
(,[,{) is met, push it onto the stack. - Whenever a right bracket (
),],}) is met, if the stack is empty the matching fails; otherwise pop the top element and match it with the right bracket, and fail if they do not match. - After the scan, if the stack is empty then everything matches; otherwise there are left brackets that were never closed.
- Every character is pushed and popped at most once, so the time complexity is and the space complexity of the stack is .
Answer:
Use the stack to record the left brackets that are still waiting to be closed, pair a right bracket with the left bracket on the top of the stack, and treat an empty stack at the end of the scan as a complete match. The time complexity is and the space complexity is .
A stack is a last in, first out linear list, a concept so simple that it hardly needs any explanation, but it is its three classic applications, bracket matching, expression evaluation and function calls, that give it its value in real systems. The terminology table aligns the notions of sequential stack, linked stack and shared stack with their English names, and the key takeaways list the complexity, the overflow conditions and the solution for the frequently asked question of whether a given pop sequence is legal.
Terminology Reference
| Term | Abbreviation | Explanation |
|---|---|---|
| Stack | - | A last in, first out linear list |
| Top | - | The end at which insertion and deletion are allowed |
| Bottom | - | The fixed end of the stack |
| Push | - | Inserting an element at the top |
| Pop | - | Removing the top element |
| Sequential stack | - | A stack implemented with an array |
| Linked stack | - | A stack implemented with a linked list |
| Shared stack | - | Two stacks sharing one array space |
| Last in, first out | LIFO | Last In First Out |
Key Takeaways
- All basic stack operations, namely push, pop and reading the top element, have a time complexity of
- A sequential stack must guard against stack overflow, while a linked stack never becomes full
- Bracket matching, expression evaluation and function calls are the three classic applications of the stack
- Judging whether a pop sequence is legal is a frequently asked question type, and it can be verified by simulating the stack
