一、 什么是栈
栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
1.1 特点
- 先入后出,后入先出(LIFO原则)
- 除头尾节点之外,每个元素有一个前驱,一个后继
1.2 栈常有的操作
push(element):添加一个新元素到栈顶位置。pop():移除栈顶的元素peek( ):返回栈顶的元素,不对栈做任何修改isEmpty():判断栈是否空,如果栈里没有任何元素,则返回true,否则返回falsesize():返回栈里的元素个数toString():将栈结构的内容以字符形式返回
二、js实现栈结构
2.1 创建一个基于数组的栈结构
class Stack{
constructor(){
this.items = []
}
//向栈中添加元素
push(element){
this.items.push(element)
}
// 移除栈顶元素
pop(){
return this.items.pop()
}
peek(){// 查看栈顶元素
return this.items[this.items.length - 1]
}
isEmpty(){
//判断栈是否为空
return this.items.length == 0
}
size(){
//判断栈中元素的个数
return this.items.length
}
clear(){
this.items = []
}
toString(){
//以字符串形式输出栈内数据
let resultString = ''
for (let i of this.items){
resultString += i + ' '
}
return resultString
}
}
2.2创建一个基于JavaScript对象的Stack类
从上述创建过程可以得出,我们是基于数组来存储其元素的。但是我们认真衡量下,在处理大量数据的时候,如何操作数据才能更高效呢。
例如我们在栈中查找某个元素,我们需要迭代整个元素的数据才能够找到。如果我们能直接获取元素,占用较少的内存空间,可以使用一个JavaScript对象存储所有的栈元素,并保证它的顺序遵循LIFO原则,接下来我们来实现。
class Stack{
constructor(){
this.items = {};
this.count = 0
}
//向栈中添加元素
push(element){
this.items[this.count] = element;
this.count ++
}
// 移除栈顶元素
pop(){
if(this.isEmpty()){
return undefined
}
this.count--
const result = this.items[this.count]
delete this.items[this.count]
return result
}
peek(){// 查看栈顶元素
if(this.isEmpty()){
return undefined
}
return this.items[this.count-1]
}
isEmpty(){
//判断栈是否为空
return this.count === 0
}
size(){
//判断栈中元素的个数
return this.count
}
clear(){
this.items = {}
this.count = 0
}
toString(){
//以字符串形式输出栈内数据
if(this.isEmpty()){
return ''
}
let objString = `${this.items[0]}`
for(let i = 1;i<this.count;i++){
objString = `${objString},${this.items[i]}`
}
return objString
}
}
三、用栈解决问题
从十进制到二进制
要把十进制转化为二进制,我们可以将十进制数除以2并对商取整,直到结果是0为止
function decimalToBinary(decNumber){
const stack = new Stack()
let number = decNumber
let rem = '',binaryString = ''
while(number>0){
rem = Math.floor(number%2)
stack.push(rem)
number = Math.floor(number/2)
}
while(!stack.isEmpty()){
binaryString +=stack.pop().toString()
}
return binaryString
}
进制转换算法
我们从上述代码中可以得到一定的逻辑,把十进制转换成基数(2~36)任意进制
function baseConverter(decNumber,base){
const stack = new Stack()
const digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
let number = decNumber
let rem = '',binaryString = ''
if(!(base >=2 && base<=36)){
return ''
}
while(number>0){
rem = Math.floor(number%base)
stack.push(rem)
number = Math.floor(number/base)
}
while(!stack.isEmpty()){
binaryString += digits[stack.pop()]
}
return binaryString
}
注意:
- 十进制转化为二进制时,余数是0或1;
- 十进制转化为八进制时,余数是0~7
- 十六进制时,余数是0~9,ABCDEF
- 因此我们在转化为进制数据时,考虑下数字对应的基数,即
digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
四、leetcode上相关算法题
| 题目 | leetcode 题数 | 难易程度 |
|---|---|---|
| 每日温度 | LeetCode 739 | 中等 |
| 字符串解码 | LeetCode 394 | 中等 |
| 有效的括号 | LeetCode 20 | 简单 |
| 基本计算器 II | LeetCode 227 | 中等 |
| 逆波兰表达式求值 | LeetCode 150 | 中等 |
| 有效的括号字符串 | LeetCode 678 | 中等 |
| 移掉k位数字 | LeetCode 402 | 中等 |