东风夜放花千树,更吹落,星如雨。
简述 栈是一种操作受限制的线性表,将允许进行插入、删除的一端称为栈顶,另一端称为栈底。看到这里你可能会觉得有点绕,其实数据结构很多的定义都很抽象,这是很正常的,下面我将类比一个生活中的常见实例帮助大家理解。
我们在日常生活中洗盘子的时候,摞起来的盘子就是一个典型的栈结构。不管取还是放,总是要放在盘子堆的最上面操作。如果你想从一堆盘子的中间强行取一个盘子,那就有可能酿成大祸。
定义 我们在日常生活中,一摞盘子肯定有很多个而且是连续的,所以我们首先需要一个切片,并且我们日常生活中的盘子是不能无限的摞的,这点在计算机里也是同样的,计算机里也会有 Stack Overflow 的问题。 所以我们还需要一个容量的限制元素。 并且我们还需要频繁的对栈顶元素进行操作,所以我们还需要一个标记位 top 来标记栈顶元素的索引。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package maintype Stack struct { container []int top int size int } func NewStack (size int ) *Stack { return &Stack{ container: make ([]int ,size), top: 0 , size: size, } }
初始化时要注意,我们这里返回的是一个 Stack 的指针,引用传递在系统中的开销比较小
栈的基本操作 对于一个栈来说,其基本操作分为以下四种。
Push(e E) , 将一个数据类型为 E 的元素 e 放到栈顶。
Pop() , 将栈顶元素取出。
IsFul() , 栈是否满了。
IsEmpty() , 栈是否空了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 package mainimport "fmt" type Stack struct { container []int top int size int } func NewStack (size int ) Stack { return Stack{ container: make ([]int , size), top: 0 , size: size, } } func (s *Stack) Push (e int ) bool { if s.IsFull() { return false } s.container[s.top] = e s.top++ return true } func (s *Stack) Pop () (flag bool , ret int ) { if s.IsEmpty() { return false , ret } ret = s.container[s.top-1 ] s.top-- return true , ret } func (s *Stack) IsEmpty () bool { if s.top == 0 { return true } return false } func (s *Stack) IsFull () bool { if s.top == s.size { return true } return false } func main () { stack := NewStack(3 ) fmt.Println(stack.Pop()) stack.Push(1 ) stack.Push(2 ) stack.Push(3 ) fmt.Println(stack.Pop()) fmt.Println(stack.Pop()) fmt.Println(stack.Pop()) }
栈的常见应用 浏览器中的前进后退 假设你现在是 N 年前的 Chrome 浏览器工程师, 你现在很苦恼,有的网页在打开下一个页面后就回不去上一级了,你现在急迫的想要一个后退的功能,请问要怎么样实现呢?
仔细分析之后不难发现,所谓的后退,撤销等操作,其实就是一个栈的 Pop 操作,我们每次点击的网址, 或者进行的操作,都是被程序 Push 到了一个栈中,所以一旦我们点击撤销或后退时,总是可以返回我们最近一次的操作。这就是栈的最广泛的应用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 package mainimport "fmt" type Stack struct { container []string top int size int } func NewStack (size int ) Stack { return Stack{ container: make ([]string , size), top: 0 , size: size, } } func (s *Stack) Push (e string ) bool { if s.IsFull() { return false } s.container[s.top] = e s.top++ return true } func (s *Stack) Pop () (flag bool , ret string ) { if s.IsEmpty() { return false , ret } ret = s.container[s.top-1 ] s.top-- return true , ret } func (s *Stack) IsEmpty () bool { if s.top == 0 { return true } return false } func (s *Stack) IsFull () bool { if s.top == s.size { return true } return false } func main () { back := NewStack(3 ) back.Push("www.baidu.com" ) back.Push("www.bing.com" ) back.Push("www.goole.com" ) fmt.Println(back.Pop()) fmt.Println(back.Pop()) fmt.Println(back.Pop()) }
括号匹配 在现代的 IDE 中,我们的编辑器环境是十分智能的,我少打了一个括号,IDE 就自动给我画了一条红线。是不是非常的神奇。其实这个功能也是用栈实现的。下面来说一下思路: 若遇到左括号入栈,遇到右括号时将栈顶左括号出栈,则遍历完所有括号后 stack 仍然为空;如果遍历之后 stack 不为空,那么说明有多余的左括号。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 package mainimport "fmt" type Stack struct { container []byte top int size int } func NewStack (size int ) Stack { return Stack{ container: make ([]byte ,size), top: 0 , size: size, } } func (s *Stack) Push (e byte ) bool { if s.IsFull() { return false } s.container[s.top] = e s.top++ return true } func (s *Stack) Pop () (flag bool ,ret byte ) { if s.IsEmpty() { return false ,ret } ret = s.container[s.top-1 ] s.top-- return true ,ret } func (s *Stack) IsEmpty () bool { if s.top == 0 { return true } return false } func (s *Stack) IsFull () bool { if s.top == s.size { return true } return false } func IsValid (s string ) bool { stack := NewStack(100 ) for _,v := range s { if v == '(' { stack.Push(byte (v)) } if v == ')' { if flag,top := stack.Pop(); flag == true &&top == '(' { continue } else { return false } } } if stack.IsEmpty() { return true } return false } func main () { test1 := "()()())" test2 := "((()" test3 := "()()()()" fmt.Println(IsValid(test1)) fmt.Println(IsValid(test2)) fmt.Println(IsValid(test3)) }