目录
1.前言
栈是一种常见的数据结构,栈分为顺序栈和链式栈,遵循FILO(First In Last Out)原则,先入后出。栈在平常编码时也经常出现,例如递归时常出现的栈溢出报错。
栈的示意图(图片来自网络)
在JavaScript里面没有提供栈类型,这里我们使用JavaScript自己实现栈。
栈常见的方法如下:
push():入栈
pop():出栈
peek():获取栈顶元素
isEmpty():判断栈是否为空
clear():清空栈
size():获取栈的大小
toString():栈的字符串输出
2.顺序栈
顺序栈是一段顺序顺序存储结构,这里我们使用数组实现顺序栈。
function Stack(values) {
//数据
//初始化时可传入数组或Set对象
this._item = values ? [...values] : []
//如果直接写构造函数的方法,相同方法在每个实例对象上都重新定义一次,太浪费内存。
//将方法写在prototype上,只定义一次,所有实例对象都可使用,节省内存空间。
//入栈
Stack.prototype.push = function (val) {
this._item.push(val)
}
//出栈
Stack.prototype.pop = function () {
//返回出栈的元素
return this._item.pop()
}
//获取栈顶元素
Stack.prototype.peek = function () {
//返回栈顶元素
return this._item[this._item.length - 1]
}
//栈是否为空
Stack.prototype.isEmpty = function () {
//返回布尔值
return this._item.length === 0
}
//清空栈
Stack.prototype.clear = function () {
this._item = []
}
//栈的大小
Stack.prototype.size = function () {
//返回栈的大小
return this._item.length
}
//栈的字符串输出
Stack.prototype.toString = function () {
//字符串形式返回栈
return this._item.toString()
}
}
测试
let test = new Stack()
//入栈测试
test.push(1)
test.push(2)
test.push(3)
test.push(4)
//获取栈的大小和字符串输出
console.log(test.size()); //4
console.log(test.toString()); //1,2,3,4
//出栈测试
console.log(test.pop()); //4
console.log(test.size()); //3
console.log(test.toString()); //1,2,3
console.log(test.pop()); //3
console.log(test.size()); //2
console.log(test.toString()); //1,2
//获取栈顶测试
console.log(test.peek()); //2
//清空栈测试
test.clear()
console.log(test.size()); //0
//判断栈是否为空测试
console.log(test.isEmpty()); //true
test.push(1)
console.log(test.isEmpty()); //false
测试通过
3.链式栈
链式栈使用的不是一段连续的内存空间,克服了顺序栈空间利用率不高的问题。这里使用单链表来实现链式栈。
function Stack() {
//栈底
this.head = null
//栈顶
this.top = null
//栈的大小
this.length = 0
//节点
function Node(value) {
this.value = value
this.next = null
}
//入栈
Stack.prototype.push = function (val) {
let node = new Node(val)
//当栈为空时入栈,栈底和栈顶都指向新插入节点
if (this.length === 0) {
this.head = node
this.top = node
} else {
//当栈不为空时插入,栈顶节点的next节点为新插入节点
//栈顶指向至原栈顶节点的next节点
this.top.next = node
this.top = this.top.next
}
this.length++
}
//出栈
Stack.prototype.pop = function () {
//栈为空时直接返回null
if (this.length === 0) return null
else if (this.length === 1) {
//栈的大小为1时
//栈顶和栈底指向null,栈的大小减为1,返回原栈顶的value
const value = this.top.value
this.head = null
this.top = null
this.length = 0
return value
} else {
//当栈的大小大于1时
//找到栈顶的上一个节点,将栈顶指向该节点,并将栈顶的next赋值为null
//栈的大小减1,返回原栈顶的value
const value = this.top.value
//当前节点,由于此时栈的至少有两个元素,所以此时curr可以直接指向栈底的下一个节点
let curr = this.head.next
//当前节点的上一个节点,此时为栈底
let prev = this.head
//当前节点不存在下一节点时则查找结束
//查找结束时curr即为栈顶,prev为栈顶的上一个节点
while (curr.next) {
curr = curr.next
prev = prev.next
}
//将栈顶指向prev,并将栈顶的下一节点赋值为null实现出栈
this.top = prev
this.top.next = null
//出栈成功,栈的大小减1
this.length--
return value
}
}
//获取栈顶元素
Stack.prototype.peek = function () {
//直接返回栈顶的value
return this.top.value
}
//栈是否为空
Stack.prototype.isEmpty = function () {
//判断栈的大小是否为0
return this.length === 0
}
//清空栈
Stack.prototype.clear = function () {
//和出栈时栈的大小为1的处理相同
this.head = null
this.top = null
this.length = 0
}
//获取栈的大小
Stack.prototype.size = function () {
//直接返回栈的大小
return this.length
}
//栈的字符串输出
Stack.prototype.toString = function () {
let str = ""
//栈的大小为0时返回空字符串
if (this.length === 0) return str
//栈的大小为1时返回栈低的字符式value
else if (this.length === 1) return str + this.head.value
else {
//当前查找到的节点
let curr = this.head
//从栈底遍历栈
while (curr) {
//字符串加逗号间隔
str = str + curr.value + ","
curr = curr.next
}
//返回结果时去除末尾多余的逗号
return str.slice(0, str.length - 1)
}
}
}
采用和顺序栈一样的测试
let test = new Stack()
//入栈测试
test.push(1)
test.push(2)
test.push(3)
test.push(4)
//获取栈的大小和字符串输出
console.log(test.size()); //4
console.log(test.toString()); //1,2,3,4
//出栈测试
console.log(test.pop()); //4
console.log(test.size()); //3
console.log(test.toString()); //1,2,3
console.log(test.pop()); //3
console.log(test.size()); //2
console.log(test.toString()); //1,2
//获取栈顶测试
console.log(test.peek()); //2
//清空栈测试
test.clear()
console.log(test.size()); //0
//判断栈是否为空测试
console.log(test.isEmpty()); //true
test.push(1)
console.log(test.isEmpty()); //false
测试通过
4.总结
栈的一切操作遵循先进先出原则,元素从栈顶压入,从栈顶压出。
栈作为最常见的数据结构之一,应用场景丰富,了解栈的原理有利于对数据结构和算法的学习。