Go-队列

1. 队列的应用场景

 

2. 队列的介绍

1)队列是一个有序列表,可以用数组或是链表来实现。

2)遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出。

示意图:(使用数组模拟队列示意图)

 

3.数组模拟队列

1)队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组声明如下,其中maxSize是该队列的最大容量

2)因为队列的输出、输入是分别从前后端来处理的,因此需要两个遍历front及rear分别记录队列前后端的下标,front会随着数据输出而改变,而rear则是随着数据输入而改变。

3)先完成一个非环形的队列(数组来实现)

当我们将数据存入队列时,称为“addqueue”,addqueue的处理需要有两个步骤:

(1)将尾指针往后移:rear + 1,front == rear 【空】

(2)若尾指针rear小于等于队列的最大下标MaxSize-1,则将数据存入rear所指的数组元素中,否则无法存入数据。rear == MaxSize - 1【队列满】

思路分析:

 

单向队列的实现,代码如下:

package main
import (
	"fmt"
	"errors"
	"os"
)
// 数组模拟队列
// 使用数组实现队列的思路
// 1. 创建一个数组array,是作为队列的一个字段
// 2. front初始化为-1
// 3. real表示队列尾部,初始化为-1
// 4. 完成队列的基本查找
// AddQueue  // 加入数据到队列
// GetQueue // 从队列取出数据
// showQueue // 显示队列
type Queue struct {
	maxSize int
	array [5]int // 数组 => 模拟队列
	front int // 表示指向队列首
	rear int // 表示指向队列的尾部
}
// 1. 添加数据到队列
func (this *Queue) AddQueue(val int) (err error) {
	// 先判断队列是否已满
	if this.rear == this.maxSize - 1 { // 重要的提示:0..........................此时rear是队列尾部,包含最后这个元素
		return errors.New("queue full")
	}
	this.rear++ // 将rear后移一位
	this.array[this.rear] = val
	return 
}
// 2. 显示队列,找到队首,然后到遍历到队尾
func (this *Queue) ShowQueue() {
	fmt.Println("队列当前的情况是:")
	// this.front 不包含这个队首的元素
	for i := this.front + 1; i <= this.rear; i++ {
		fmt.Printf("array[%d]=%d \t", i,this.array[i])
	}
 
}
// 3. 从队列中取出数据
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 main() {
	// 先创建一个队列
	queue := &Queue{   // 这种形式的写法,下面要调用GetQueue就不能再写成(&queue).GetQueue()
		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("加入队列ok")
				} else {
					fmt.Println(err.Error())
				}
			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)上面代码实现了基本队列结构,但是没有有效的利用数组空间

2)请思考,如何使用数组,实现一个环形队列

数组模拟环形队列

对前面的数组模拟队列的优化,充分利用数组。因此将数组看作是一个环形的。(通过取模的方式来实现即可)

提醒:

1)尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的时候需要注意(tail + 1) % maxSize == head 【满】

2)tail == head 【空】

3)分析思路如下:

(1)什么时候表示队列满(tail + 1)% maxSize == head

(2)tail == head 表示空

(3)初始化时,tail == 0,head == 0

(4)怎么统计该队列有多少个元素(tail + maxSize - head)% maxSize

package main
import (
	"fmt"
	"errors"
	"os"
)
 
// 使用一个结构体管理队列
type CircleQueue struct {
	maxSize int
	array [5]int // 数组 => 模拟队列
	head int // 指向队首
	tail int // 指向队尾
}
// 0. 添加数据到环形队列
func (this *CircleQueue) Push(val int) (err error) {
	if this.IsFull() {
		return errors.New("CirleQueue full")
	}
	this.array[this.tail] = val // 把值给尾部
	this.tail = (this.tail + 1) % this.maxSize
	return
}
func (this *CircleQueue) Pop() (val int, err error) {
	if this.IsEmpty() {
		return -1,errors.New("CircleQueue empty")
	}
	// 取出
	val = this.array[this.head]
	this.head++
	return val, nil
}
// 2. 判断环形队列是否满了
func (this *CircleQueue) IsFull()bool {
	return (this.tail + 1) % this.maxSize == this.head
}
// 3. 判断环形队列是否为空
func (this *CircleQueue) IsEmpty() bool {
	return this.tail == this.head
}
// 4. 取出环形队列有多少个元素
func (this *CircleQueue) Size() int {
	return (this.tail + this.maxSize - this.head) % this.maxSize
}
 
// 5. 显示环形队列
func (this *CircleQueue) Show() {
	// 取出当前队列有多少个元素
	size := this.Size()
	if size == 0 {
		fmt.Println("队列为空")
	}
	// 设计一个辅助的变量,指向head
	tempHead := this.head
	for i := 0; i < size; i++ {
		fmt.Printf("arr[%d]=%d\t",tempHead,this.array[tempHead])
		tempHead = (tempHead + 1) % this.maxSize
	}
	fmt.Println()
}
 
func main() {
	var circleQueue = &CircleQueue{
		maxSize: 5,
		head: 0,
		tail: 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 := circleQueue.Push(val)
				if err != nil {
					fmt.Println(err.Error())
				} else {
					fmt.Println("加入队列 ok")
				}
			case "get":
				val, err := circleQueue.Pop()
				if err != nil {
					fmt.Println(err.Error())
				} else {
					fmt.Println("从队列中取出了一个数=",val)
				}
			case "show":
				circleQueue.Show()
			case "exit":
				os.Exit(0)
		}
	}
}

  

posted @ 2022-09-18 15:49  南昌拌粉的成长  阅读(559)  评论(0编辑  收藏  举报