Using Stacks in JavaScript
In JavaScript, the stack is a very useful data structure that follows the Last In First Out (LIFO) principle. In this article, we'll take a closer look at the concept of the stack and how it's used in JavaScript.
I. The concept of stack
A stack is a linear data structure that can only perform insertion (called in-stack or press, push) and deletion (called out-stack or pop, pop) operations at one end. Imagine a stack of plates, you can only take plates from the top (out of the stack) or put them on the top (into the stack).
Stacks usually have the following basic operations:
-
push(element)
: Presses an element onto the top of the stack. -
pop()
: Pop the top stack element and return it. -
peek()
: View the top stack element without popping it. -
isEmpty()
: Determine if the stack is empty.
Implementing the stack in JavaScript
The following code implements a simple stack in JavaScript:
class Stack {
constructor() {
= [];
}
push(element) {
(element);
}
pop() {
if (()) {
return "Underflow";
}
return ();
}
peek() {
if (()) {
return null;
}
return [ - 1];
}
isEmpty() {
return === 0;
}
size() {
return ;
}
}
III. Practical use of stacks
(i) Expression evaluation
-
Suffix to Suffix Expressions
In computer science, converting a prefix expression to a suffix expression is an important application of stacks. A prefix expression is the form of arithmetic expression that we usually use, such as(2 + 3) * 4
. Suffix expressions, on the other hand, place the operator after the operand, for example2 3 + 4 *
。
The steps of the algorithm are as follows:
- Initialize an empty stack for storing operators.
- Iterates over the midfix expression from left to right.
- If an operand is encountered, it is output directly.
- If left brackets are encountered, they are pressed onto the stack.
- If a right parenthesis is encountered, pop the operator on the stack and output it until a left parenthesis is encountered, then discard the left parenthesis.
- If an operator is encountered, it is processed according to its priority. If the priority of the top-of-stack operator is higher than or equal to the current operator, the top-of-stack operator is popped and output; otherwise, the current operator is pressed onto the stack.
- At the end of the traversal, the remaining operators in the stack are popped in order and output.
The following is the code that implements the conversion of a medial expression to a postfix expression in JavaScript:
function infixToPostfix(expression) {
const stack = new Stack();
let postfix = "";
const precedence = {
'+': 1,
'-': 1,
'*': 2,
'/': 2
};
for (let char of expression) {
if (/[0-9]/.test(char)) {
postfix += char;
} else if (char === '(') {
(char);
} else if (char === ')') {
while (!() && ()!== '(') {
postfix += ();
}
(); // Pop-up left brackets
} else {
while (!() && precedence[()] >= precedence[char]) {
postfix += ();
}
(char);
}
}
while (!()) {
postfix += ();
}
return postfix;
}
-
Suffix expression evaluation
Once you have converted a midfix expression to a suffix expression, it is easy to evaluate the suffix expression.
The steps of the algorithm are as follows:
- Iterate over the suffix expression from left to right.
- If an operand is encountered, it is pressed onto the stack.
- If an operator is encountered, the two operands on the stack are popped, the corresponding operation is performed, and the result is pressed back onto the stack.
- At the end of the traversal, the only element on the stack is the result of the expression.
The following code implements suffix expression evaluation in JavaScript:
function evaluatePostfix(postfix) {
const stack = new Stack();
for (let char of postfix) {
if (/[0-9]/.test(char)) {
(parseInt(char));
} else {
const operand2 = ();
const operand1 = ();
switch (char) {
case '+':
(operand1 + operand2);
break;
case '-':
(operand1 - operand2);
break;
case '*':
(operand1 * operand2);
break;
case '/':
(operand1 / operand2);
break;
}
}
}
return ();
}
(ii) Function call stack
In JavaScript, when a function calls another function, a structure called a Call Stack is created in memory. A call stack is a stack data structure that is used to keep track of the order in which functions are called.
Example:
function functionA() {
("Inside functionA");
functionB();
}
function functionB() {
("Inside functionB");
}
functionA();
(coll.) fail (a student)functionA
is called, its execution context is pressed onto the call stack. When thefunctionA
call (programming)functionB
whenfunctionB
execution context is also pressed into the call stack. When thefunctionB
After execution, its execution context is popped off the call stack. Then.functionA
continues to execute until it too is executed and its execution context is popped off the call stack.
This mechanism ensures the correct execution order of functions and scope management of variables.
(iii) Depth-first search (DFS)
Depth-first search is a graph traversal algorithm that can be implemented using a stack.
The following code implements depth-first search in JavaScript:
class Graph {
constructor() {
= {};
}
addVertex(vertex) {
if (![vertex]) {
[vertex] = [];
}
}
addEdge(vertex1, vertex2) {
[vertex1].push(vertex2);
[vertex2].push(vertex1);
}
dfs(startVertex) {
const stack = new Stack();
const visited = {};
(startVertex);
visited[startVertex] = true;
while (!()) {
const currentVertex = ();
(currentVertex);
for (let neighbor of [currentVertex]) {
if (!visited[neighbor]) {
(neighbor);
visited[neighbor] = true;
}
}
}
}
}
It can be called using the following:
const graph = new Graph();
('A');
('B');
('C');
('D');
('E');
('A', 'B');
('A', 'C');
('B', 'D');
('C', 'E');
('A');
In this example, depth-first search starts from a given starting vertex and uses a stack to store the vertices to be visited. Each time a vertex is popped from the stack, it is visited and its unvisited neighbor vertices are pressed into the stack.
(iv) Bracket matching
Checking to see if the parentheses in a string match is another common application of stacks.
The steps of the algorithm are as follows:
- Initialize an empty stack.
- Iterates over each character in the string.
- If left brackets are encountered, they are pressed onto the stack.
- If a right parenthesis is encountered, check whether the stack is empty. If it is empty, the right parenthesis has no matching left parenthesis, return false. if the stack is not empty, pop the top element of the stack and check if the popped left parenthesis matches the current right parenthesis. If not, return false.
- At the end of the traversal, if the stack is empty, indicating that all parentheses match, return true; otherwise, return false.
The following code implements bracket matching in JavaScript:
function isBalanced(str) {
const stack = new Stack();
for (let char of str) {
if (char === '(' || char === '[' || char === '{') {
(char);
} else if (char === ')' || char === ']' || char === '}') {
if (()) {
return false;
}
const top = ();
if ((char === ')' && top!== '(') || (char === ']' && top!== '[') || (char === '}' && top!== '{')) {
return false;
}
}
}
return ();
}
IV. Summary
The stack is a powerful data structure with many practical applications in JavaScript. From expression evaluation to function call stacks, from graph traversal to bracket matching, the stack plays an important role. Understanding the concepts and operations of stacks, and how to implement and apply them in JavaScript, is very helpful for writing efficient code and solving various programming problems.