关于“栈”,有一个非常贴切的例子,就是一摞叠在一起的盘子。我们平时放盘子的时候,都是从下往上一个一个放;取的时候,我们也是从上往下一个一个地依次取,不能从中间任意抽出。后进者先出,先进者后出,这就是典型的“栈”结构。
从栈的操作特性上来看,栈是一种“操作受限”的线性表,只允许在一端插入和删除数据。
问题是,相比数组和链表,栈带给我们的只有限制,并没有任何优势,为什么不直接使用数组或者链表呢?为什么非得用这个“操作受限”的“栈”呢?
从刚才栈的定义里,我们可以看出,栈主要包含两个操作,入栈和出栈,也就是在栈顶插入一个数据和从栈顶删除一个数据。理解了栈的定义之后,我们来看一看如何用代码实现一个栈。
实际上,栈既可以用数组来实现,也可以用链表来实现。
下面用java实现了一个数组栈
// 基于数组实现的顺序栈
public class ArrayStack {
private String[] items; // 数组
private int count; // 栈中元素个数
private int n; // 栈的大小
// 初始化数组,申请一个大小为 n 的数组空间
public ArrayStack(int n) {
this.items = new String[n];
this.n = n;
this.count = 0;
}
// 入栈操作
public boolean push(String item) {
// 数组空间不够了,直接返回 false,入栈失败。
if (count == n) return false;
// 将 item 放到下标为 count 的位置,并且 count 加一
items[count] = item;
++count;
return true;
}
// 出栈操作
public String pop() {
// 栈为空,则直接返回 null
if (count == 0) return null;
// 返回下标为 count-1 的数组元素,并且栈中元素个数 count 减一
String tmp = items[count-1];
--count;
return tmp;
}
}
了解了定义和基本操作,那它的操作的时间、空间复杂度是多少呢?
上面的数组栈,是一个固定大小的栈,也就是说,在初始化栈的时候需要事先指定栈的大小。当栈满之后,就无法再往栈里添加数据了。尽管链式栈的大小不受限,但要存储next指针,内存消耗相对比较多。那我们如果基于数组实现一个可以支持动态扩容的栈呢?
要实现一个支持动态扩容的栈,我们只需要底层依赖一个支持动态扩容的数组就可以了。。当栈满了之后,我们就申请一个更大的数组,将原来的数据搬移到新数组中。如下:
实际上,支持动态扩容的顺序栈,我们平时开发中并不常用到。我们重点来看一下复杂度分析
(1) 对于出栈操作来说,我们不会涉及内存的重新申请和数据的搬移,所以出栈的时间复杂度仍然是O(1)。但是,对于出栈操作来说,当栈中有空闲空间时,入栈操作的时间复杂度是O(1);当空间不够时,就需要重新申请内存和数据搬移,所以时间复杂度就是O(n)
(2)也就是说,对于入栈操作来说,最好情况时间复杂度是O(1),最坏情况时间复杂度就是O(n)。
(3)那平均情况时间复杂度又是多少呢?
为了分析的方便,我们需要事先做一些假设和定义
如果当前栈大小为k,并且已满,当再有新的数据要入栈时,就需要重新申请2倍大小的内存,并且做k个数据的搬移操作,然后再入栈。但是,接下来的k-1次入栈操作,我们都不需要再重新申请内存和搬移数据,所以这k-1次入栈操作都只需要一个 simple-push操作就可以完成。
可以看的出来,这K次入栈操作,总共涉及了K个数据的搬移,以及K次simple-push操作。将k个数据搬移均摊到K次入栈操作,那每个入栈操作只需要一个数据搬移和一个simple-push操作。以此类推,入栈操作的均摊时间复杂度就位O(1)
通过这个例子的实战分析,也印证了均摊时间复杂度一般都等于最好情况时间复杂度这个结论。因为在大部分情况下,入栈操作的时间复杂度O都是O(1),只有在个别时刻才会退化为O(n),所以把耗时多的入栈操作的时间均摊到其他入栈操作上,平均情况下的耗时就会接近O(1)
我们知道,操作系统给每个线程分配了一块独立的内存空间,这块内存被组织成“栈”这种结构,用来存储函数调用时的临时变量。每进入一个函数,都会将临时变量作为一个栈帧入栈,当被调用函数执行完成,返回之后,将这个函数对应的栈帧出栈。
看个例子
int main() {
int a = 1;
int ret = 0;
int res = 0;
ret = add(3, 5);
res = a + ret;
printf("%d", res);
reuturn 0;
}
int add(int x, int y) {
int sum = 0;
sum = x + y;
return sum;
}
从代码中我们可以看出,main() 函数调用了 add() 函数,获取计算结果,并且与临时变量a 相加,最后打印 res 的值。在执行到add() 函数时,函数调用栈的情况如下:
为什么函数调用要用“栈”来保存临时变量呢?用其他数据结构不行吗?
举个例子,对于算术表达式,计算机是怎么计算的呢?
比如对于将 3+5*8-6,计算过程如下:
假设表达式中只包含三种括号,圆括号 ()、方括号 [] 和花括号{},并且它们可以任意嵌套。比如,{[{}]}或 [{()}([])] 等都为合法格式,而{[}()] 或 [({)] 为不合法的格式。那现在给你一个包含三种括号的表达式字符串,如何检查它是否合法呢?
#include
#include
using namespace std;
//
bool isValid (string s){
if(s.empty()){
return true;
}
if(s.length() % 2 != 0){
return false;
}
stack<char> stack;
for (int i = 0; i < s.size(); ++i) {
if(s[i] == '(' || s[i] == '{' || s[i] == '['){
stack.push(s[i]);
}else{
if(stack.size() == 0){
return false;
}
char c = stack.top();
stack.pop();
char match;
if(s[i] == ')'){
match = '(';
}else if(s[i] == '}'){
match = '{';
}else {
match = ']';
}
if(c != match){
return false;
}
}
}
return stack.empty();
}
申请两个栈,X 和 Y,
比如你顺序查看了 a,b,c 三个页面,我们就依次把 a,b,c 压入栈,这个时候,两个栈的数据就是这个样子:
当你通过浏览器的后退按钮,从页面 c 后退到页面 a 之后,我们就依次把 c 和 b 从栈 X 中弹出,并且依次放入到栈 Y。这个时候,两个栈的数据就是这个样子:
这个时候你又想看页面 b,于是你又点击前进按钮回到 b 页面,我们就把 b 再从栈 Y 中出栈,放入栈 X 中。此时两个栈的数据是这个样子:
这个时候,你通过页面 b 又跳转到新的页面 d 了,页面 c 就无法再通过前进、后退按钮重复查看了,所以需要清空栈 Y。此时两个栈的数据这个样子:
slice := []int{1, 2, 3, 4};
value := -1
slice = append([]int{value}, slice...)
slice := []int{1, 2, 3, 4};
value := -1
slice = append(slice, value)
make([]interface{}, 0)
和make([]interface{}, 10)
的区别,这个对s.values = append(s.values, item)
有很大的影响
slice = slice[1:]
slice := []int{1, 2, 3, 4};
pos := 2 // 待删除元素的位置
slice = append(slice[:pos], slice[pos + 1:]...)
stack.go
package main
import "fmt"
type Stack struct {
values []interface{} // 用来存储数据
top int // 栈顶元素索引
}
func NewStack() *Stack {
return &Stack{
values: make([]interface{}, 0),
top: -1,
}
}
// 放置元素
func (s *Stack)Push(item interface{}) {
s.values = append(s.values, item)
s.top++
}
// 获取栈顶元素但是不删除它
func (s *Stack)Peek()(interface{}, error) {
if s.top < 0 {
return nil, fmt.Errorf("[ERR] Index out of range")
}
item := s.values[s.top]
return item, nil
}
// 栈中多少个元素
func (s *Stack)Size() int {
return s.top + 1
}
// 弹出栈顶元素
func (s *Stack) Pop()(interface{}, error) {
if s.top < 0 {
return nil, fmt.Errorf("[ ERR ] Index out of range")
}
item := s.values[s.top]
s.values = append(s.values[:s.top], s.values[s.top + 1:]...)
s.top--
return item, nil
}
stack_test.go
package main
import "testing"
func TestStackPushAndPop(t *testing.T) {
s := NewStack()
s.Push("test_1")
if v, err := s.Pop(); err == nil {
if "test_1" != v && s.top == 0 {
t.Errorf("Failed to Pop")
}
} else {
t.Errorf("Failed to Pop")
}
if s.top != -1 {
t.Errorf("The top should be -1. Instead: %d", s.top)
}
s.Push("test_2")
s.Push("test_3")
if s.top != 1 {
t.Errorf("The top should be 1. Instead: %d", s.top)
}
if v, err := s.Pop(); err == nil {
if "test_3" != v && s.top == 1 {
t.Errorf("Failed to Pop")
}
} else {
t.Errorf("Failed to Pop")
}
}
func TestStackSize(t *testing.T) {
s := NewStack()
if s.Size() != 0 {
t.Errorf("The size of the stack should be 0. Instead %d", s.Size())
}
s.Push("Test")
s.Push("Test_2")
s.Push("Test_3")
if s.Size() != 3 {
t.Errorf("The size of the stack should be 3. Instead %d", s.Size())
}
}
package main
import (
"fmt"
)
type StackArray interface {
Push(v interface{}) error
Peek() (interface{}, error)
Pop()(interface{}, error)
Get() []interface{}
Size() int
IsEmpty() bool
Clear()
IsFull() bool //是否满了
}
type Stack struct {
values []interface{} // 用来存储数据
top int // 栈顶元素索引
capacity int // 栈最多能够存放多少元素,默认不限制
}
func NewStack() *Stack{
return &Stack{
values: make([]interface{}, 0),
top: -1,
capacity: -1,
}
}
func NewStackSize(capacity int) *Stack {
return &Stack{
values: make([]interface{}, capacity),
top: -1,
capacity: capacity,
}
}
func (s *Stack) Size() int {
return s.top + 1
}
func (s *Stack) IsEmpty() bool {
return s.top == -1
}
func (s *Stack) IsFull() bool{
if s.capacity == -1 {
return false
}
return s.Size() == s.capacity
}
func (s *Stack)Clear() {
s.top = -1
s.values = []interface{}{}
}
func (s *Stack) Push(v interface{}) error {
if s.IsFull() {
return fmt.Errorf("[ERR] Stack full");
}
//s.values = append(s.values, v) --- error []
s.values[s.top + 1] = v
s.top++;
return nil
}
func (s *Stack) Peek() (interface{}, error) {
if s.IsEmpty() {
return nil, fmt.Errorf("[ERR] Stack is empty")
}
item := s.values[s.top]
return item, nil
}
// 弹出最顶端的数据
func (s *Stack)Pop()(interface{}, error) {
if s.IsEmpty(){
return nil, fmt.Errorf("[ERR] Stack is empty")
}
oldValue := s.values[s.top]
s.values[s.top] = nil
//s.values = append(s.values[:s.top], s.values[s.top + 1:]...)
s.top--
return oldValue, nil
}
// 返回所有压入的数据
func (s *Stack)Get() []interface{} {
return s.values[:s.top + 1]
}
func (s *Stack) SetSize(capacity int) error {
if capacity < 0 {
return fmt.Errorf("capacity must >= 0");
}
s.capacity = capacity
if capacity < s.Size() {
s.values = s.values[:capacity] // 只保留前capacity个元素
s.top = capacity - 1
}
return nil
}
func main() {
//var stack StackArray;
var stack *Stack;
stack = NewStackSize(12)
stack.Push(1)
stack.Push(2)
stack.Push(3)
stack.Push(1)
stack.Push(2)
stack.Push(3)
stack.Push(1)
stack.Push(2)
stack.Push(3)
stack.Push(1)
stack.Push(2)
stack.Push(3)
//stack.Clear()
//fmt.Println(stack.IsFull(), "--------" , stack.Size())
//
//for i := 3; i > 0; i--{
// item, _ := stack.Pop()
//
// fmt.Println(item)
//}
stack.SetSize(3);
fmt.Println(stack.Get())
}
package main
import (
"fmt"
"sync"
)
var errEmptyStack = fmt.Errorf("[ERR], stack is empty\n")
type Stack struct {
stack []interface{}
len int
lock sync.Mutex
}
func NewStack()*Stack {
return &Stack{
stack: make([]interface{}, 0),
len: 0,
lock: sync.Mutex{},
}
}
// 数组头部插入
func (s *Stack)Push(v interface{}) {
s.lock.Lock()
defer s.lock.Unlock()
s.stack = append([]interface{}{v}, s.stack...) // ...是必须的
s.len++
}
func (s *Stack) Peek() (interface{}, error) {
s.lock.Lock()
defer s.lock.Unlock()
if s.len == 0 {
return nil, errEmptyStack
}
return s.stack[0], nil
}
func (s *Stack)Pop() (interface{}, error) {
s.lock.Lock()
defer s.lock.Unlock()
if s.len == 0 {
return nil, errEmptyStack
}
var item interface{}
item, s.stack = s.stack[0], s.stack[1:]
s.len--
return item, nil
}
func (s *Stack)Len() int{
s.lock.Lock()
defer s.lock.Unlock()
return s.len
}
func (s *Stack)Empty() bool{
return s.len == 0
}
测试
package main
import (
"fmt"
"testing"
)
func TestNew(t *testing.T) {
s := NewStack()
if !s.Empty() || s.len != 0 || s.Len() != 0 {
t.Error()
}
s.Push(1)
s.Push(2)
s.Push(3)
if s.stack[0] != 3 ||
s.stack[1] != 2 ||
s.stack[2] != 1 {
fmt.Println(s.stack)
t.Error()
}
if s.Len() != 3 {
t.Error()
}
a, _ := s.Pop()
if a != 3 || s.Len() != 2 {
t.Error()
}
b, _ := s.Peek()
if b != 2 {
t.Error()
}
}
package main
import (
"fmt"
)
// 先进先出
type myInterface interface {
Push(data interface{})
Pop() (interface{}, error)
Peek() (interface{}, error)
Clear()
Print()
Empty() bool
}
type node struct {
data interface{}
next *node
}
func newNode(data interface{}) *node {
return &node{
data: data,
next: nil,
}
}
type LinkedStack struct {
top *node
}
func New() *LinkedStack {
return &LinkedStack{
top: newNode(nil),
}
}
func (l *LinkedStack)Empty() bool{
return l.top.next == nil
}
func (l *LinkedStack)Peek() (interface{}, error){
if l.Empty() {
return nil, fmt.Errorf("stack empty");
}
return l.top.next.data, nil
}
func (l *LinkedStack)Push(data interface{}){
if data == nil {
return
}
node := newNode(data)
node.next = l.top.next;
l.top.next = node
}
func (l *LinkedStack) Pop() (interface{}, error){
if l.Empty() {
return nil, fmt.Errorf("stack empty");
}
// 至少有一个
res := l.top.next
l.top.next = l.top.next.next
res.next = nil
return res.data, nil
}
func (l *LinkedStack)Clear(){
l.top.next = nil
}
func (l *LinkedStack)Print(){
curr := l.top.next
for curr != nil{
fmt.Print(curr.data);
fmt.Printf("\t<---\t");
curr = curr.next
}
if peek, err := l.Peek(); err == nil {
fmt.Print("|\t" , peek)
}
fmt.Println()
}
func main(){
ls := New();
for i := 0; i < 10; i++{
ls.Push(i);
}
ls.Print();
for i := 0; i < 12; i++{
_, _ = ls.Pop()
ls.Print();
}
}
低质量代码。对于链表节点,无论什么操作,都请用哑节点来简化操作
package main
import "fmt"
type stackItem struct {
item interface{}
next *stackItem
}
type Stack struct {
sp *stackItem
depth int // 元素个数
}
func NewStack() *Stack {
return &Stack{
sp: nil,
depth: 0,
}
}
// 放置元素
func (s *Stack)Push(v interface{}) error {
// 新元素的next指向原来的栈顶元素
item := &stackItem{
item: v,
next: s.sp,
}
// 新元素变成栈顶元素
s.sp = item
s.depth++
return nil
}
// 获取栈顶元素但是不删除它
func (s *Stack)Peek()(interface{}, error) {
if s.depth <= 0 {
return nil, fmt.Errorf("[ ERR ] Stack is empty")
}
return s.sp.item, nil
}
// 栈中多少个元素
func (s *Stack)Size() int {
return s.depth
}
// 弹出栈顶元素
func (s *Stack) Pop()(interface{}, error) {
if s.depth <= 0 {
return nil, fmt.Errorf("[ ERR ] Stack is empty")
}
// 拿到栈顶元素
item := s.sp.item
// 修改栈顶元素
s.sp = s.sp.next
s.depth--
return item, nil
}
func (s *Stack) IsEmpty() bool {
return s.depth == 0
}
测试
func TestStack(t *testing.T) {
var stack *Stack = NewStack()
stack.Push(1)
stack.Push(2)
stack.Push(3)
stack.Push(4)
stack.Push(5)
for i := 5; i > 0; i-- {
item, _ := stack.Pop()
if item != i {
t.Error("TestStack failed...", i)
}
}
}