![3c636e5206d1b02519e18bb3919b9576.png](https://img-blog.csdnimg.cn/img_convert/3c636e5206d1b02519e18bb3919b9576.png)
数据结构和算法是我自己薄弱的地方,也是每个程序员绕不开的话题,于是想着每周末学习一个数据结构或算法,并通过 leetcode 进行练习,于是就想通过专栏记录下自己的学习过程
栈:属于数据结构,是一种后进先出(LIFO)的有序集合。在栈中,进行新元素的存取的一端属于栈顶,另一端是栈底。越靠近栈底的是旧元素,靠近栈顶的是新元素,栈只允许在一段进行元素的存放和取出。
![0cbee50d0cc7f498d69c46a91cfa5e7b.png](https://img-blog.csdnimg.cn/img_convert/0cbee50d0cc7f498d69c46a91cfa5e7b.png)
生活中有许多常见的列子,比如,在餐厅中叠放盘子,放盘子和取盘子都是在最上面(栈顶)进行的。
栈的应用也有很多,比如浏览器的前进和后退功能,编译器和内存存放变量和方法的调用等。
下面我们来创建栈:
由于栈遵从后进先出的原则,我们需要选择一种数据结构来实现栈,这里我们使用数组来创建,考虑到数组具备了对一端进行操作的方法,能够方便我们快速创建,也因为栈只能限制在一端操作,我们为此创建几个方法:
- push():将元素存入栈顶
- pop():将元素从栈顶中移除
- peek():查看栈顶中最新的一个元素
- isEmpty():判断栈是否为空
- size():查看栈的大小和数组的长度类似
- clear():清空栈
- print():一个辅助方法,输出栈中的元素
class
向栈中增加元素的方法:
push (elements) {
this.items.push(elements)
}
使用push方法,能够将我们的元素保存在栈中。
向栈中移除元素的方法:
pop () {
return this.items.pop()
}
使用pop方法,将元素从栈中移除。push和pop都是在数组的一端操作元素的方法,这里就能够实现在栈的一端操作元素。
查看栈顶元素:
peek () {
return this.items[this.items.length - 1]
}
栈顶相当于数组中的最后一位元素,所以查看栈顶,实际是获取数组最后一个元素,元素的最后的索引是数组长度减一。
判断是否为空:
isEmpty () {
return this.items.length === 0
}
通过判断数组长度是否为空,如果为空返回true,不为空返回false。
获取栈的长度:
size () {
return this.items.length
}
size方法类似数组的length,对于集合,我们使用size来代替length,查看集合的大小。
清空栈:
clear () {
this.items = []
}
将栈清空的最简单的方式,就是直接赋值为空就行,当然也可以一个个移除。
辅助输出元素方法:
print () {
return this.items.toString()
}
我们简单的数组转为字符串输出,当然也可以使用循环格式化输出,具体实现,可以根据自己喜好进行(●'◡'●)
至此,我们的栈就完成了,现在我们来测试一下:
const stack = new Stack()
console.info(stack.isEmpty()) // true
stack.push(1)
stack.push(2)
console.log(stack.peek()) //2
stack.push(3)
stack.push(4)
console.log(stack.size()) //4
stack.pop()
stack.pop()
console.info(stack.size()) //2
![7b56af77e6ba18fddd7db1ebf579da0d.png](https://img-blog.csdnimg.cn/img_convert/7b56af77e6ba18fddd7db1ebf579da0d.png)
当我们对栈操作的时候,变化过程如上图,先是在栈顶push两个元素1和2,这时候栈(如图中的Stack-2)的size变为2,在push元素3和4,栈(如图中的Stack-3)的size变为4,之后进行pop操作,将元素4和3从栈顶移除,栈(如图中的Stack-5)最后只剩下元素1和2,size为2。
现在我们完成了通过数组创建了栈,当然我们也可以使用对象来创建栈,代码如下:
class Stack_Object {
constructor () {
this._items = {}
this.count = 0 // 记录对象中的元素个数类似数组的length
}
push (element) {
this._items[this.count++] = element
}
pop () {
if (!this.count) {
return undefined
}
const res = this._items[--this.count]
delete this._items[this.count]
return res
}
peek () {
return this._items[this.count - 1]
}
isEmpty () {
return this.count === 0
}
size () {
return this.count
}
clear () {
this._items = {}
this.count = 0
}
// 辅助方法
print () {
if (this.isEmpty()) {
return ''
}
let str = `${this._items[0]}`
for (let i = 1; i < this.count; i++) {
str += `,${this._items[i]}`
}
return str
}
}
使用对象来记录的不同点是,我们需要自己维护一个变量count来统计当前栈中的大小相当于数组的length。我们可以使用上面的测试代码来进行测试:
const stack = new Stack_Object()
console.info(stack.isEmpty()) // true
stack.push(1)
stack.push(2)
console.log(stack.peek()) //2
stack.push(3)
stack.push(4)
console.log(stack.size()) //4
stack.pop()
stack.pop()
console.info(stack.size()) //2
结果和上面的测试代码一致,至此,我们通过object创建栈也已经实现了。
至此,我们学习完成了栈的特点,总结一下:
- 栈是一个受限线性结构,遵从后进先出的原则
- 栈只能在栈顶操作元素,复杂度为O(1)
既然了解了栈,就需要及时对它进行练习,下面列举了几道leetcode上面的练习题,从简单到复杂,及时练习才是最好的效果。
- 包含min函数的栈
2. 有效的括号
3. 验证栈序列
4. 不同字符的最小子序列
5. 去除重复字母
6. 柱状图中最大的矩形
每日一练,让自己幸福满满(#^.^#)