一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情。
栈的概念
栈是一个数据结构,特色是"先入先出"。这种数据结构特别适合用来实现函数。所以我们一般说,函数栈,函数栈。
用一个数组来实现栈
我们假设现在的数据类型只有int,也就是说,我们存取的都是整数。那么就可以用一个数组来实现栈。
一开始的时候,数组是空的,我们还用到一个额外的变量top来存储当前栈顶在哪里。
- 对于push操作,我们存入数据之后,我们就将栈顶变量top++
- 对于pop操作,我们取出数据之后,我们就将栈顶变量top--
- push操作之前,需要判断栈是否已经满了
- pop操作之间,需要判断栈是否已经空了
根据上面的逻辑,很容易就能用一个数组来实现栈。
双栈的概念
双栈就是两个独立的栈,两个栈的 push pop, 互不影响。
这种数据结构可以用来实现一个队列数据结构
用一个数组来实现双栈
简单实现
我们知道一个数组可以用来实现一个栈,那么思路就是:
- 将这个数组分成前后两个部分,前面实现一个栈,后面实现一个栈
缺点:
不能充分利用空间。
比如说,一个长度是6的数组,本来总的可以存储6个元素的。
现在往第一个栈里只能存三个,当我继续push第一个栈的时候,就出错了。
而此时,数组还剩三个空闲位置,所以不能充分利用空间。
优化实现
两个栈各自有自己的栈顶变量top1和top2
- 第一个栈从数组开头往中间push,top1++
- 第二个栈从数组结尾往中间push,top2--
两个栈push的时候,可以计算top1和top2之间的空余,来判断栈是否满了。
优化实现示例代码(Golang)
type DualStack struct {
arr []int
top1, top2 int
length int
}
func NewDualStack(length int) *DualStack {
ds := new(DualStack)
ds.arr = make([]int, length)
ds.length = length
ds.top1 = -1
ds.top2 = ds.length
return ds
}
// 往第一个栈 push value
func (dualStack *DualStack) push1(value int) {
if dualStack.top1 < dualStack.top2-1 {
dualStack.top1++
dualStack.arr[dualStack.top1] = value
} else {
panic("stack 1 over flow")
}
}
// 往第二个栈 push value
func (dualStack *DualStack) push2(value int) {
if dualStack.top1 < dualStack.top2-1 {
dualStack.top2--
dualStack.arr[dualStack.top2] = value
} else {
panic("stack 2 over flow")
}
}
// 第一个栈的 pop 操作
func (dualStack *DualStack) pop1() int {
if dualStack.top1 >= 0 {
var value = dualStack.arr[dualStack.top1]
dualStack.top1--
return value
} else {
panic("stack 1 under flow")
}
}
// 第二个栈的 pop 操作
func (dualStack *DualStack) pop2() int {
if dualStack.top2 < dualStack.length {
var value = dualStack.arr[dualStack.top2]
dualStack.top2++
return value
} else {
panic("stack 1 under flow")
}
}