hello world

stay foolish, stay hungry

数据结构--栈

栈(stack)是限制插入和删除只能在一个位置上进行的表,该位置叫表的末端,或者叫栈顶(top)。由于对栈中所有元素的操作都是在栈顶,所以栈有后进先出(LIFO)的特性。

图 1

栈的基本操作有:

  • 元素入栈操作 push
  • 元素出栈操作 pop
  • 查看栈顶元素 top

入栈是指在栈顶插入元素,出栈是指删除栈顶元素。

栈的实现

通过链表实现

可以使用单链表来实现栈,链表的前端作为栈顶,通过在链表头插入元素来实现 push,删除表头元素来实现 pop,top 操作只是返回链表头部元素的指。

import (
    "container/list"
    "sync"
)
type Stack struct {
    sync.RWMutex
    l *list.List
}
func New() *Stack {
    s := &Stack{
        l: list.New(),
    }
    return s
}
func (s *Stack) Push(v interface{}) {
    s.Lock()
    defer s.Unlock()
    s.l.PushFront(v)
}
func (s *Stack) Pop() interface{} {
    s.Lock()
    defer s.Unlock()
    ele := s.l.Front()
    return s.l.Remove(ele)
}
func (s *Stack) Top() interface{} {
    s.RLock()
    defer s.RUnlock()
    return s.l.Front()
}

通过 slice 实现

使用 slice 实现栈比较简单,先声明一个初始容量比较大的 slice,空栈时栈顶 index(top) 可以设置为 -1,有新元素 x 入栈操作,可以将栈顶index(top) 的值加 1,然后设置 stack[top]=x;元素出栈操作,可以先返回 stack[top],然后将栈顶 index(top) 的值减 1。

import (
    "sync"
)
type Stack struct {
    sync.RWMutex
    s   []interface{}
    top int
    c   int
}
func New(capacity int) *Stack {
    s := &Stack{
        s:   make([]interface{}, capacity),
        c:   capacity,
        top: -1,
    }
    return s
}
func (s *Stack) Push(v interface{}) {
    s.Lock()
    defer s.Unlock()
    if s.top+1 > s.c {
        panic("stack over flow")
    }
    s.top++
    s.s[s.top] = v
}
func (s *Stack) Pop() interface{} {
    s.Lock()
    defer s.Unlock()
    if s.top == -1 {
        panic("empty stack")
    }
    ele := s.s[s.top]
    s.top--
    return ele
}
func (s *Stack) Top() interface{} {
    s.RLock()
    defer s.RUnlock()
    if s.top == -1 {
        panic("empty stack")
    }
    return s.s[s.top]
}

总结

栈实现简单,效率很高,通常 push 和 pop 都是 O(1) 的操作。错误检测和线程安全方面的考虑可能会拖慢栈的执行效率,对空栈的 pop 和对满栈的 push 可能都会导致程序异常。在golang中实现栈还有一个问题是,golang目前没有支持泛型(generic)或者类型参数(type params),所以编写通用的数据结构只能使用 interface,这样带来的问题是,无法在数据结构的实现中来保证数据类型的安全,使用时需要多加注意。

参考资料

  1. 数据结构与算法分析