Go - 队列 Queue
队列介绍
1、队列是一个有序列表,可以用数组或是链表来实现。
2、遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出
数组模拟队列
1、队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列的数组声明如下,其中 MaxSize 是该队列的最大容量。
2、因为队列的输入、输出是分别从前后端处理的,因此需要两个变量 front 和 rear 来分别记录队列前后端的小标,front 会随着数据输出而改变,而 rear 则是随着数据输入而改变
如下图所示:
先完成一个非环形的队列(数组来实现)
当我们将数据存入队列时称为“addqueue”,addqueue 的处理需要两个步骤:
1、将尾指针往后移,rear + 1 ,front = rear 【空】
2、若尾指针 rear 小于等于队列的最大下标 MaxSize - 1 ,则将数据,存入 rear 所指向的数组元素中,否则无法存入数据。rear = MaxSize - 1 【队列满】
使用数组实现队列
1、创建一个数组 arrary , 是作为队列的一个字段
2、front 初始化为-1
3、real , 表示队列尾部,初始化为 -1
4、完成队列的基本查找:
AddQueue //加入数据到队列
GetQueue //从队列取出数据
ShowQueue //显示队列
代码如下:
package main import ( "errors" "fmt" "os" ) // 使用一个结构体管理队列 type Queue struct { maxSize int array [5]int // 数组=>模拟队列 front int // 表示指向队列首 rear int //表示指向队列的尾部 } // 添加数据到队列 func (this *Queue) AddQueue(val int) (err error) { // 先判断队列是否已满 if this.rear == this.maxSize-1 { // 重要重要的提示; rear 是队列尾部(含最后元素) return errors.New("queue full") } this.rear++ this.array[this.rear] = val return } // 从队列中取出数据 func (this *Queue) GetQueue() (val int, err error) { // 先判断队列是否为空 if this.front == this.rear { // 队空 return -1, errors.New("queue empty") } this.front++ val = this.array[this.front] return val, err } // 显示队列, 找到队首,然后到遍历到队尾 func (this *Queue) ShowQueue() { fmt.Println("队列当前的情况是:") for i := this.front + 1; i <= this.rear; i++ { fmt.Printf("array[%d]=%d\t", i, this.array[i]) } fmt.Println() } func main() { // 先创建一个队列 queue := &Queue{ maxSize: 5, front: -1, rear: -1, } var key string var val int for { fmt.Println("1. 输入add 表示添加数据到队列") fmt.Println("2. 输入get 表示从队列获取数据") fmt.Println("3. 输入show 表示显示队列") fmt.Println("4. 输入exit 表示显示队列") fmt.Scanln(&key) switch key { case "add": fmt.Println("输入你要入队列数") fmt.Scanln(&val) err := queue.AddQueue(val) if err != nil { fmt.Println(err.Error()) } else { fmt.Println("加入队列ok") } case "get": val, err := queue.GetQueue() if err != nil { fmt.Println(err.Error()) } else { fmt.Println("从队列中取出了一个数=", val) } case "show": queue.ShowQueue() case "exit": os.Exit(0) } } }
上面代码实现了基本队列结构,但是没有有效的利用数组空间
数组模拟环形队列
对前面的数组模拟队列的优化,充分利用数组,因此将数组看做是一个环形的。(通过取模的方式可以实现)
分析思路
1、什么时候表示队列满? (rear + 1)% maxSize = front
2、front = rear 【空】
3、初始化时,front = 0 rear = 0
4、怎么统计队列有多少个元素:
(rear + maxSize - front)%maxSize
遇到的问题(循环队列空一个元素的位置)
在得到队列满足的条件:(rear+1)%maxSize ==front 时,怎么算都感觉在 rear 已经表示最后元素的后一位了,这种情况下 +1 肯定是不对的,查到一些资料:
那么,循环队列为什么用一个空一个元素的位置呢???
这个是根据需要来用的 循环队列中,由于入队时尾指du针向前追赶头指针;zhi出队时头指针向前追赶尾指针,造成dao队空和队满时头尾指针均相等。因此,无法通过条件front==rear来判别队列是"空"还是"满"。 解决这个问题的方法至少有三种: ①另设一布尔变量以区别队列的空和满; ②少用一个元素的空间。约定入队前,测试尾指针在循环意义下加1后是否等于头指针,若相等则认为队满(注意:rear所指的单元始终为空); ③使用一个计数器记录队列中元素的总数(即队列长度)。
代码如下:
package main import ( "errors" "fmt" "os" ) // 使用一个结构体管理环形队列 type CircleQueue struct { maxSize int // 最多存储 maxSize - 1 个元素 array [5]int // 数组 front int //指向队列队首 0 rear int //指向队尾 0 } // 入队列 AddQueue(push) GetQueue(pop) func (this *CircleQueue) Push(val int) (err error) { if this.IsFull() { return errors.New("queue full") } //分析出this.rear(tail) 在队列尾部,但是包含最后的元素 this.array[this.rear] = val //把值给尾部 this.rear = (this.rear + 1) % this.maxSize return } //出队列 func (this *CircleQueue) Pop() (val int, err error) { if this.IsEmpty() { return 0, errors.New("queue empty") } //取出,front(head) 是指向队首,并且含队首元素 val = this.array[this.front] this.front = (this.front + 1) % this.maxSize return } //显示队列 func (this *CircleQueue) ListQueue() { fmt.Println("环形队列情况如下:") //取出当前队列有多少个元素 size := this.Size() if size == 0 { fmt.Println("队列为空") } //设计一个辅助的变量,指向head tempHead := this.front for i := 0; i < size; i++ { fmt.Printf("arr[%d]=%d\t", tempHead, this.array[tempHead]) tempHead = (tempHead + 1) % this.maxSize } fmt.Println() } //判断环形队列为满 func (this *CircleQueue) IsFull() bool { return (this.rear+1)%this.maxSize == this.front } //判断环形队列是空 func (this *CircleQueue) IsEmpty() bool { return this.rear == this.front } //取出环形队列有多少个元素 func (this *CircleQueue) Size() int { //这是一个关键的算法. return (this.rear + this.maxSize - this.front) % this.maxSize } func main() { // 初始化一个环形队列 queue := &CircleQueue{ maxSize: 5, front: 0, rear: 0, } var key string var val int for { fmt.Println("1. 输入add 表示添加数据到队列") fmt.Println("2. 输入get 表示从队列获取数据") fmt.Println("3. 输入show 表示显示队列") fmt.Println("4. 输入exit 表示显示队列") fmt.Scanln(&key) switch key { case "add": fmt.Println("输入你要入队列数") fmt.Scanln(&val) err := queue.Push(val) if err != nil { fmt.Println(err.Error()) } else { fmt.Println("加入队列ok") } case "get": val, err := queue.Pop() if err != nil { fmt.Println(err.Error()) } else { fmt.Println("从队列中取出了一个数=", val) } case "show": queue.ListQueue() case "exit": os.Exit(0) } } }