@@ -0,0 +1,288 @@
|
|||||||
|
# 队列
|
||||||
|
|
||||||
|
!!! note "💡 一句话概述"
|
||||||
|
队列是一种先进先出(FIFO)的数据结构,只允许从一端入队、另一端出队。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔑 核心概念
|
||||||
|
|
||||||
|
1. **FIFO 原则**:先入队的元素先出队,类似于排队买票
|
||||||
|
2. **操作**:入队(Enqueue)从队尾添加,出队(Dequeue)从队头移除
|
||||||
|
3. **类型**:顺序队列、链式队列、循环队列、双端队列
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 详细说明
|
||||||
|
|
||||||
|
队列是一种受限的线性表,限制只在表的两端进行操作:
|
||||||
|
|
||||||
|
- **队头(Front)**:允许出队的一端
|
||||||
|
- **队尾(Rear)**:允许入队的一端
|
||||||
|
|
||||||
|
**适用场景**:任务调度、消息队列、BFS 遍历、缓冲区、打印队列等。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💻 代码示例
|
||||||
|
|
||||||
|
### 顺序队列(数组实现)
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Queue struct {
|
||||||
|
data []int
|
||||||
|
head int
|
||||||
|
tail int
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewQueue(capacity int) *Queue {
|
||||||
|
return &Queue{
|
||||||
|
data: make([]int, capacity),
|
||||||
|
head: 0,
|
||||||
|
tail: 0,
|
||||||
|
size: capacity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) Enqueue(val int) bool {
|
||||||
|
if q.tail == q.size {
|
||||||
|
return false // 队满
|
||||||
|
}
|
||||||
|
q.data[q.tail] = val
|
||||||
|
q.tail++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) Dequeue() (int, bool) {
|
||||||
|
if q.head == q.tail {
|
||||||
|
return 0, false // 队空
|
||||||
|
}
|
||||||
|
val := q.data[q.head]
|
||||||
|
q.head++
|
||||||
|
return val, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queue) IsEmpty() bool {
|
||||||
|
return q.head == q.tail
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
q := NewQueue(5)
|
||||||
|
q.Enqueue(1)
|
||||||
|
q.Enqueue(2)
|
||||||
|
q.Enqueue(3)
|
||||||
|
|
||||||
|
for !q.IsEmpty() {
|
||||||
|
val, _ := q.Dequeue()
|
||||||
|
fmt.Println(val) // 输出: 1 2 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 链式队列(链表实现)
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Node struct {
|
||||||
|
Val int
|
||||||
|
Next *Node
|
||||||
|
}
|
||||||
|
|
||||||
|
type LinkedQueue struct {
|
||||||
|
head *Node
|
||||||
|
tail *Node
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLinkedQueue() *LinkedQueue {
|
||||||
|
return &LinkedQueue{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *LinkedQueue) Enqueue(val int) {
|
||||||
|
node := &Node{Val: val}
|
||||||
|
if q.tail != nil {
|
||||||
|
q.tail.Next = node
|
||||||
|
}
|
||||||
|
q.tail = node
|
||||||
|
if q.head == nil {
|
||||||
|
q.head = node
|
||||||
|
}
|
||||||
|
q.size++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *LinkedQueue) Dequeue() (int, bool) {
|
||||||
|
if q.head == nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
val := q.head.Val
|
||||||
|
q.head = q.head.Next
|
||||||
|
if q.head == nil {
|
||||||
|
q.tail = nil
|
||||||
|
}
|
||||||
|
q.size--
|
||||||
|
return val, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
q := NewLinkedQueue()
|
||||||
|
q.Enqueue(10)
|
||||||
|
q.Enqueue(20)
|
||||||
|
|
||||||
|
for {
|
||||||
|
val, ok := q.Dequeue()
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
fmt.Println(val) // 输出: 10 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 循环队列
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type CircularQueue struct {
|
||||||
|
data []int
|
||||||
|
head int
|
||||||
|
tail int
|
||||||
|
size int
|
||||||
|
cap int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCircularQueue(capacity int) *CircularQueue {
|
||||||
|
return &CircularQueue{
|
||||||
|
data: make([]int, capacity),
|
||||||
|
head: 0,
|
||||||
|
tail: 0,
|
||||||
|
size: 0,
|
||||||
|
cap: capacity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *CircularQueue) Enqueue(val int) bool {
|
||||||
|
if q.size == q.cap {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
q.data[q.tail] = val
|
||||||
|
q.tail = (q.tail + 1) % q.cap
|
||||||
|
q.size++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *CircularQueue) Dequeue() (int, bool) {
|
||||||
|
if q.size == 0 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
val := q.data[q.head]
|
||||||
|
q.head = (q.head + 1) % q.cap
|
||||||
|
q.size--
|
||||||
|
return val, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
q := NewCircularQueue(3)
|
||||||
|
q.Enqueue(1)
|
||||||
|
q.Enqueue(2)
|
||||||
|
q.Enqueue(3)
|
||||||
|
|
||||||
|
val, _ := q.Dequeue() // 出队 1
|
||||||
|
fmt.Println("出队:", val)
|
||||||
|
|
||||||
|
q.Enqueue(4) // 入队 4
|
||||||
|
|
||||||
|
for !q.IsEmpty() {
|
||||||
|
val, _ := q.Dequeue()
|
||||||
|
fmt.Println(val) // 输出: 2 3 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 常见陷阱
|
||||||
|
|
||||||
|
!!! warning "顺序队列的假溢出"
|
||||||
|
用数组实现队列时,即使前面有空位,tail 到达数组末尾也无法入队。解决办法:使用循环队列,通过取模运算让队列"首尾相接"。
|
||||||
|
|
||||||
|
!!! warning "队空和队满的判断条件"
|
||||||
|
循环队列中,`head == tail` 表示队空,`(tail + 1) % cap == head` 表示队满(牺牲一个位置)。或者额外维护一个 `size` 变量来区分。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏋️ 练习题
|
||||||
|
|
||||||
|
??? question "练习 1:用两个栈实现队列"
|
||||||
|
使用两个栈实现队列的 push 和 pop 操作。
|
||||||
|
|
||||||
|
```go
|
||||||
|
type MyQueue struct {
|
||||||
|
inStack []int
|
||||||
|
outStack []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *MyQueue) Push(x int) {
|
||||||
|
q.inStack = append(q.inStack, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *MyQueue) Pop() int {
|
||||||
|
if len(q.outStack) == 0 {
|
||||||
|
for len(q.inStack) > 0 {
|
||||||
|
q.outStack = append(q.outStack, q.inStack[len(q.inStack)-1])
|
||||||
|
q.inStack = q.inStack[:len(q.inStack)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val := q.outStack[len(q.outStack)-1]
|
||||||
|
q.outStack = q.outStack[:len(q.outStack)-1]
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
??? question "练习 2:用队列实现 BFS"
|
||||||
|
用队列实现二叉树的层序遍历。
|
||||||
|
|
||||||
|
??? success "答案"
|
||||||
|
```go
|
||||||
|
func levelOrder(root *TreeNode) [][]int {
|
||||||
|
if root == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
result := [][]int{}
|
||||||
|
queue := []*TreeNode{root}
|
||||||
|
|
||||||
|
for len(queue) > 0 {
|
||||||
|
level := []int{}
|
||||||
|
size := len(queue)
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
node := queue[0]
|
||||||
|
queue = queue[1:]
|
||||||
|
level = append(level, node.Val)
|
||||||
|
if node.Left != nil {
|
||||||
|
queue = append(queue, node.Left)
|
||||||
|
}
|
||||||
|
if node.Right != nil {
|
||||||
|
queue = append(queue, node.Right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = append(result, level)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔗 相关链接
|
||||||
|
|
||||||
|
- [Go container/list](https://pkg.go.dev/container/list) — 标准库双向链表,可用于实现队列
|
||||||
@@ -101,3 +101,4 @@ nav:
|
|||||||
- 布谷鸟过滤器: algorithm/cuckoo-filter.md
|
- 布谷鸟过滤器: algorithm/cuckoo-filter.md
|
||||||
- HeavyKeeper: algorithm/heavykeeper.md
|
- HeavyKeeper: algorithm/heavykeeper.md
|
||||||
- 二叉树的遍历: algorithm/binary-tree-traversal.md
|
- 二叉树的遍历: algorithm/binary-tree-traversal.md
|
||||||
|
- 队列: algorithm/queue.md
|
||||||
|
|||||||
Reference in New Issue
Block a user