简介
编辑循环队列就是将
队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用。在循环队列结构中,当存储空间的最后一个位置已被使用而再要进入队运算时,只需要存储空间的第一个位置空闲,便可将元素加入到第一个位置,即将存储空间的第一个位置作为队尾。
循环队列可以更简单防止伪溢出的发生,但队列大小是固定的。
在循环队列中,当队列为空时,有front=rear,而当所有队列空间全占满时,也有front=rear。
为了区别这两种情况,规定循环队列最多只能有MaxSize-1个队列元素,当循环队列中只剩下一个空存储单元时,队列就已经满了。
因此,队列判空的条件是front=rear,而队列判满的条件是front=(rear+1)%MaxSize
maxSize=5
初始 head=0 tail=0
----------------------------------------------------------------
0 1 2 3 4
-----------------------------------------------------------------
| | | | | |
队满情况1 我们先一次性先放满
head=0 tail=[1,2,3,4] 放入元素后this.tail 才变动 [ this.tail = (this.tail + 1) % this.maxSize ]
再放就放不进去了 因为队满 (this.tail+1)%this.maxSize == this.head==(4+1)%5=0
----------------------------------------------------------------
0 1 2 3 4
-----------------------------------------------------------------
| * | * | * | * | |
只有先取出一个 从队首开始取 取出元素后this.head才变动 取出前head=0 tail=4
取出后 head= this.head = (this.head + 1) % this.maxSize=(0+1)%this.maxSize=1 tail=4
----------------------------------------------------------------
0 1 2 3 4
-----------------------------------------------------------------
| | * | * | * | |
再取一个 从队首开始取 取出元素后this.head才变动 取出前head=1 tail=4
取出后 head= this.head = (this.head + 1) % this.maxSize=(1+1)%this.maxSize=2 tail=4
----------------------------------------------------------------
0 1 2 3 4
-----------------------------------------------------------------
| | | * | * | |
再取一个 从队首开始取 取出元素后this.head才变动 取出前head=2 tail=4
取出后 head= this.head = (this.head + 1) % this.maxSize=(2+1)%this.maxSize=3 tail=4
----------------------------------------------------------------
0 1 2 3 4
-----------------------------------------------------------------
| | | | * | |
此时 再取一个 从队首开始取 取出元素后this.head才变动 取出前head=3 tail=4
取出后 head= this.head = (this.head + 1) % this.maxSize=(3+1)%this.maxSize=4 tail=4
此时hea=tail 根据环形队列规则 这种情况就是队空
----------------------------------------------------------------
0 1 2 3 4
-----------------------------------------------------------------
| | | | | |
此时我们再放入一个元素 放入前 head=4 tail=4
队满条件不会触发 (this.tail+1)%this.maxSize == (4+1)%5 =0 != this.head 所以可以放入
从队尾放开始放入 放入后tail才变动
放入后: head=4 this.tail = (this.tail + 1) % this.maxSize=(4+1)%5=0
----------------------------------------------------------------
0 1 2 3 4
-----------------------------------------------------------------
| | | | | * |
再放入一个 放入前 head=4 tail=0
放入后: head=4 this.tail = (this.tail + 1) % this.maxSize=(0+1)%5=1
----------------------------------------------------------------
0 1 2 3 4
-----------------------------------------------------------------
| * | | | | * |
由此可见 head和tail都要规避数组越界 即存入和取出时都会+1然后和总长度取模
再放入一个 放入前 head=4 tail=1
放入后: head=4 this.tail = (this.tail + 1) % this.maxSize=(1+1)%5=2
----------------------------------------------------------------
0 1 2 3 4
-----------------------------------------------------------------
| * | * | | | * |
队满情况2再放入一个 放入前 head=4 tail=2
放入后: head=4 this.tail = (this.tail + 1) % this.maxSize=(2+1)%5=3
----------------------------------------------------------------
0 1 2 3 4
-----------------------------------------------------------------
| * | * | * | | * |
再放就放不进去了 因为队满 (this.tail+1)%this.maxSize == this.head==(3+1)%5=4=head
只有先取出一个 从队首开始取 取出元素后this.head才变动 取出前head=4 tail=3
取出后 head= this.head = (this.head + 1) % this.maxSize=(4+1)%this.maxSize=0 tail=3
----------------------------------------------------------------
0 1 2 3 4
-----------------------------------------------------------------
| * | * | * | | |
队满情况3再放入一个 放入前 head=0 tail=3
放入后: head=0 this.tail = (this.tail + 1) % this.maxSize=(3+1)%5=4
----------------------------------------------------------------
0 1 2 3 4
-----------------------------------------------------------------
| * | * | * | * | |
再放就放不进去了 因为队满
(this.tail+1)%this.maxSize == this.head==(4+1)%5=0=head
队列中总共的元素个数: ([0,(整数A-1)) + 整数A)%整数A 其取值范围在[0,整数A)
(this.tail - this.head + this.maxSize) % this.maxSize
最后一种情况长度: (3-4+5)%5=(-1+5)%5=4%5=4
遍历分两种情况(这是特别要注意的地方)
head
head>tail 这是规避数组越界情况发生的遍历 比如最后一种情况head=4 tail=3
package main
import (
"errors"
"fmt"
"os"
)
// 使用一个结构体管理队列
type CircleQueue struct {
maxSize int //5
array []int
head int //指向队首 0 有效元素的前一个位置
tail int //指向队尾 0
}
func NewCircleQueue(maxSize int) *CircleQueue {
queue := &CircleQueue{
maxSize: maxSize,
array: make([]int, maxSize),
head: 0,
tail: 0,
}
return queue
}
// 入队
func (this *CircleQueue) Push(val int) error {
if this.IsFull() {
return errors.New("队满")
}
fmt.Println("入队前: head=", this.head, "tail=", this.tail, "len=", this.Size())
//从队尾放
this.array[this.tail] = val
//重新设置队尾标志位 有可能又到前面0的位置
this.tail = (this.tail + 1) % this.maxSize
fmt.Println("入队后: head=", this.head, "tail=", this.tail, "len=", this.Size())
return nil
}
// 出队
func (this *CircleQueue) Pop() (int, error) {
if this.IsEmpty() {
return -1, errors.New("队空")
}
fmt.Println("出队前: head=", this.head, "tail=", this.tail, "len=", this.Size())
//从队首取
val := this.array[this.head]
this.array[this.head] = 0
//head指向队首 且包含队首 有可能又到前面0的位置
this.head = (this.head + 1) % this.maxSize
fmt.Println("出队后: head=", this.head, "tail=", this.tail, "len=", this.Size())
return val, nil
}
// 判断队满
func (this *CircleQueue) IsFull() bool {
return (this.tail+1)%this.maxSize == this.head
}
// 判断队空
func (this *CircleQueue) IsEmpty() bool {
return this.tail == this.head
}
// 获取环形队列有多少个元素
func (this *CircleQueue) Size() int {
return (this.tail - this.head + this.maxSize) % this.maxSize
}
// 显示队列
func (this *CircleQueue) ListQueue() {
size := this.Size()
if size <= 0 {
fmt.Println("队空 显示不了")
return
}
fmt.Println("list: head=", this.head, "tail=", this.tail, "len=", size)
if this.head < this.tail {
for i := this.head; i < this.tail; i++ {
fmt.Printf("arr[%d]=%d \n", i, this.array[i])
}
} else { // this.head>this.tail 规避数组越界情况发生的遍历
if this.tail-1 >= 0 {
for j := 0; j < this.tail; j++ {
fmt.Printf("arr[%d]=%d \n", j, this.array[j])
}
}
for i := this.head; i < this.maxSize; i++ {
fmt.Printf("arr[%d]=%d \n", i, this.array[i])
}
}
}
func main() {
// //创建队列
queue := NewCircleQueue(5)
var key string
var val int
for {
fmt.Println(" 输入push表示添加数据到队列")
fmt.Println(" 输入pop表示从队列获取数据")
fmt.Println(" 输入list表示显示队列数据")
fmt.Println(" 输入exit表示退出")
fmt.Scanln(&key)
switch key {
case "push":
fmt.Println("请输入入队数据")
fmt.Scanln(&val)
err := queue.Push(val)
if err != nil {
fmt.Println("Push err: ", err.Error())
} else {
fmt.Println("Push ok")
}
case "pop":
val, err := queue.Pop()
if err != nil {
fmt.Println("Pop err: ", err.Error())
} else {
fmt.Println("Pop ok val=", val)
}
case "list":
queue.ListQueue()
case "exit":
os.Exit(0)
}
}
//fmt.Println("OK")
}