栈(stack)是一种遵循先入后出逻辑的线性数据结构。

我们可以把它想象成吃零食的薯片桶,打开薯片桶之后,每次只能拿到最上面的薯片。如图所示,我们把堆叠元素的顶部称为”栈顶“,底部元素称为”栈底“。将把元素添加到栈顶的操作叫做“入栈",删除栈顶元素的操作叫做“出栈”。


栈操作

栈的常用操作

通常情况下,我们可以直接使用编程语言内置的栈类。 然而某些语言可能并不提供专门的栈类,这时我们可以将该语言的“数组”或“链表”当作栈来使用,并在逻辑上忽略栈无关的操作。
栈的常用操作有

  • push(), 元素入栈,将元素添加至栈顶,时间复杂度O(1)
  • pop(), 元素出栈,将栈顶元素删除,时间复杂度尾O(1)
  • peek(),访问栈顶元素,时间复杂度O(1)
var stack []int
// 元素入栈
stack = append(stack, 1)
stack = append(stack,2)

// 元素出栈
pop := stack[len(stack)-1]
stack = stack[:len(stack)-1]

// 访问栈顶元素
peek := stack[len(stack)-1]

// 获取栈的长度
size := len(stack)

// 是否为空判断
isEmpty := len(stack) == 0

栈的实现

栈遵循先入后出的原则,因此我们只能在栈顶添加或删除元素,然而,数组和链表都可以在任意位置添加和删除元素,因此栈可以视为一种受限制的数组或链表.换句话说,我们可以屏蔽数组或链表的部分无关操作,使其对外表现得逻辑符合栈的特性。

1. 基于链表的实现

使用链表实现栈时,我们可以将链表头节点视为栈顶,尾节点视为栈底。因为链表是顺序访问的,头节点是我们最先访问的元素,尾节点是最后访问的元素,这个栈的特性一致。
对于入栈操作,我们只需将元素插入链表头部,这种插入方法成为”头插法“。
对于出栈操作,我们只需将头节点从链表中删除。

package main

import (
    "container/list"
    "fmt"
)

type Stack struct {
    *list.List
}

func NewStack() *Stack {
    return &Stack{
        List: list.New(),
    }
}

// 入栈,将元素添加至栈顶,也就是链表的头节点
func (s *Stack) Push(element interface{}) {
    s.List.PushBack(element)
}

// 出栈,将栈顶元素删除,
func (s *Stack) Pop() interface{} {
    if s.IsEmpty() {
        return nil
    }
    e := s.List.Back()
    s.List.Remove(e)
    return e.Value
}

func (s *Stack) Peek() interface{} {
    return s.List.Front()
}

func (s *Stack) Size() int {
    return s.List.Len()
}

func (s *Stack) IsEmpty() bool {
    return s.List.Len() == 0
}

func main() {
    var s = NewStack()
    s.Push(1)
    s.Push(2)
    s.Push(3)
    fmt.Println(s.Pop(), s.Size())
    fmt.Println(s.Pop(), s.Size())
    fmt.Println(s.Pop(), s.Size())
}
/* 输出结果
3
3 2
2 1
1 0
*/

1. 基于数组的实现

使用数组实现栈时,我们可以将数组的尾部作为栈顶,入栈和出栈操作均在数组尾部进行。时间复杂度为O(1)

type StackArray struct {
    data []interface{}
}

func NewStackArray() *StackArray {
    return &StackArray{}
}

func (s *StackArray) Push(element interface{}) {
    s.data = append(s.data, element)
}

// 出栈,将栈顶元素删除,
func (s *StackArray) Pop() interface{} {
    if s.IsEmpty() {
        return nil
    }

    e := s.data[len(s.data)-1]
    s.data = s.data[:len(s.data)-1]
    return e
}

func (s *StackArray) Peek() interface{} {
    if s.IsEmpty() {
        return nil
    }
    return s.data[len(s.data)-1]
}

func (s *StackArray) Size() int {
    return len(s.data)
}

func (s *StackArray) IsEmpty() bool {
    return len(s.data) == 0
}

两种实现对比

时间效率

在基于数组的实现中,入栈和出栈操作都在预先分配好的连续内存中进行,具有很好的缓存本地性,因此效率较高。如果入栈时超出数组容量,会触发扩容机制,导致该入栈操作的时间复杂度变为O(n)。
在基于链表的实现中,链表的扩容十分灵活,不会出现扩容导致的效率地下问题。但是入栈操作需要初始化节点对象并修改指针,因此效率相对较低。
综上所述,当入栈与出栈操作的元素是基本数据类型时,我们可以得出以下结论:

  • 基于数组的实现的栈在触发扩容机制时效率会降低,但由于扩容时低频操作,因此平均效率更高。
  • 基于链表实现的栈可以提供更加稳定的效率表现。

栈的典型应用

程序内存管理
每次调用函数时,系统都会在栈顶添加一个栈帧,用于记录函数的上下文信息,在递归函数中,向下递推阶段会不断执行入栈操作,而向上回溯阶段则会不断执行出栈操作。

©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,884评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,212评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,351评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,412评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,438评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,127评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,714评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,636评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,173评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,264评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,402评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,073评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,763评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,253评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,382评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,749评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,403评论 2 358

推荐阅读更多精彩内容