Summary
In this article, I will introduce the basic operations of the data structure Stack and some of its applications.
We will see the application of Stack matching detection in parentheses, expression evaluation, and function call.
Recursion is a special function call. Because recursion is very important and difficult to understand in programming, I will explain my understanding of recursion.
Finally, we will see how Stack and Recursion are used to elegantly solve a classic game: Tower of death.
This article also provides an HTML5 demonstration of expression evaluation and tower of Hanoi.
Stack Introduction
Stack is the Stack, which is defined in Wikipedia:
In computer science, it is a special serial form of data structure. It is special in that it can only be called as a stack top indicator at the end of the chain or serial array) to add the information English: push) and output information English: pop) operation. In addition, the stack can also be completed in the form of a one-dimensional array or a serial link.
According to the definition, we know that there are only three stack operations: push, pop, and top ). In addition, the stack can only manipulate the top elements of the stack, that is, operations can only be performed at one end.
Because the stack has the nature of the First pop-up elements, the stack is also called the structure of the Last In First Out (LIFO, Last In First Out.
Stack operations are very simple. We can use a single list and an array to implement the stack.
However, in JavaScript, Array comes with pop () and push () operations, and we can use Array [Array. length-1] to implement top () operations. Therefore, there is no need to implement another Stack type, which can be expressed in Array.
Application
The features of Stack LIFO make it suitable for solving many practical problems. Below we will discuss three of its applications.
Bracket matching detection
When we write code in the editor, some editors will automatically check whether the brackets match before and after. If they do not match, an error will be reported.
With the LIFO feature of Stack, we can easily implement this function.
The pseudo code of the algorithm is as follows:
- // Create a Stack s
- S = new stack ()
- // Read characters until they are read
- While read to c! = EOF:
- // If the character is open brackets such as '(' ['{', etc.
- If c is opening:
- S. push (c)
- // If the character is an ending bracket, such as ') '']''}'
- Else if c is closing:
- // If the stack is empty or the top element of the stack does not match the opening brackets, an error is returned.
- If s is empty or f s. pop () is not correspond to c:
- Return error!
- // If the last stack is not empty, an error is returned.
- If s is not empty:
- Return error!
- // If no error is returned, return normal
- Return OK
The principle of the algorithm is that when we encounter an ending bracket, we always need to find whether the last open bracket matches it. If we cannot find the ending bracket, or if the last open parentheses do not match, an error is returned.
Because we always and only need to find the last element, we will open the brackets into the stack, and when matching, it will go out of the stack.
Due to Stack features, this algorithm is simple and clear and consumes a linear O (n) time complexity ).
Evaluate expressions
The powerful feature of Stack also enables it to be used in expression evaluation.
Suppose an expression: 2 + 4/(3-1)
This expression has three types of symbols:
The calculation algorithm is as follows:
- // Allocate two stacks. ops is the operator stack, and nums is the digital stack.
- Ops = new Stack, nums = new Stack
- // Read characters from the expression until the end
- While read c in expression! = EOF:
- // If it is left parenthesis, the input operator Stack
- If c is '(':
- Ops. push (c)
- // If it is a number, add it to the digital stack.
- Else if c is a number:
- Nums. push (c)
- // If it is an operator
- Else if c is an operator:
- // If the top element of the operator Stack has a higher or the same priority as that of c, a single operation is performed.
- While ops. top () is equal or precedence over c:
- Op = ops. pop ()
- Opn2 = nums. pop ()
- Opn1 = nums. pop ()
- // Perform a single operation and import the operation count to the digital Stack
- Nums. push (cal (op, opn1, opn2 ))
- // If it is a right Brace
- Else if c is ')':
- // Unless the top element of the stack is left parenthesis, the operator stacks out of the stack and adds the calculation result to the digital stack.
- Op = ops. pop ()
- While op! = '(':
- Opn2 = nums. pop ()
- Opn1 = nums. pop ()
- Nums. push (cal (op, opn1, opn2 ))
- Op = ops. pop ()
- // Return the stack top element of the digital Stack
- Return nums. top ()
The following is a DEMO of expression evaluation:
Function call
When debugging code, we often find the following error messages when encountering function errors:
- /Users/tim/Codes/JavaScript/dsaginjs/DSinJS/Stack/InfixExpression.js:59
- return prioty[a] ) prioty[b];
- ^
- SyntaxError: Unexpected token )
- at Module._compile (module.js:439:25)
- at Object.Module._extensions..js (module.js:474:10)
- at Module.load (module.js:356:32)
- at Function.Module._load (module.js:312:12)
- at Function.Module.runMain (module.js:497:10)
- at startup (node.js:119:16)
- at node.js:902:3
In fact, we just got an error in one place. Why do we print so many error messages?
The reason is that the interpreter prints out the stacks of all called functions that reported the error.
When calling a function, we need to switch to the called function, but once the function call ends, we have to return to the original position.
Using the stack, we can implement this in an orderly manner, that is, when the function is called, we put the current function variable and context into the stack, and wait until the function call ends, and then we exit the stack, get the previous context and variable.