上一篇博客讲了数组和链表,它们有一个很大的区别就是在内存中的存在方式是不同的。数组是连续的,链表是零散的
。这种在内存中的存在形式又被称为物理结构
。数组是顺序存储结构,链表是链式存储结构
。除了物理结构之外,还有一个叫做逻辑结构
,分为线性结构(顺序表,栈,队列)和非线性结构(树,图)
。
之所以叫做逻辑结构是因为我们不关心它在内存的存储形式,可能是顺序存储,也可能是链式存储。我们只关心数据结构本身的特性
比如栈和队列的物理实现既可以用数组实现,也能用链表实现
。
这里我们只关心栈和队形本身的逻辑特性
,即栈是先进后出,队列是先进先出
。
栈的相关概念:
- 先进后出
- 最早进入的元素存放的位置叫做
栈底
,最后进入的元素存放的位置叫做栈顶
,栈底和栈顶都对应一个元素 - 入栈和出栈的时间复杂度都是O(1)的。入栈append很好理解,出栈涉及到slice的截取,在底层实现是数组指针的移动,也是O(1)的
用数组实现栈比较简单,代码如下:
package main
import (
"errors"
"fmt"
)
type Stack struct {
arr []interface{} //切片
stackSize int //栈中元素的个数
}
func NewStack()Stack {
stack:=Stack{arr:make([]interface{},0)}
return stack
}
//push栈元素
func (s *Stack) push(t interface{}) {
s.arr = append(s.arr, t)
s.stackSize += 1
}
//pop栈元素
func (s *Stack) pop() interface{} {
if s.stackSize > 0 { //栈不为空时
s.stackSize--
element := s.arr[s.stackSize]
s.arr = s.arr[:s.stackSize]
return element
}
return errors.New("栈为空")
}
func main() {
stack:=NewStack()
stack.push(8)
fmt.Println(stack.pop())
}
队列的相关概念:
- 先进先出
- 队列的出口端叫做队头,队列的入口端叫做队尾
- 入列和出列的时间复杂度都是O(1)的。入列append很好理解,出列涉及到slice的截取,在底层实现是数组指针的移动,也是O(1)的
package main
import (
"errors"
"fmt"
)
// 队列,以切片的最后一个元素为对头,第一个元素为队尾
type Queen struct {
arr []interface{} //切片
stackSize int //栈中元素的个数
}
func NewQueen()Queen {
stack:=Queen{arr:make([]interface{},0)}
return stack
}
//push队列元素
func (s *Queen) push(t interface{}) {
s.arr = append(s.arr, t)
s.stackSize += 1
}
//pop队列元素
func (s *Queen) pop() interface{} {
if s.stackSize > 0 { //队列不为空时
element := s.arr[0]
s.arr = s.arr[1:s.stackSize]
s.stackSize--
return element
}
return errors.New("队列为空")
}
func main() {
stack:=NewQueen()
stack.push(8)
fmt.Println(stack.pop())
}
参考资料:
Go 数据结构和算法篇(二):栈