https://www.cnblogs.com/zly-go/p/15472059.html
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端进行删除操作,而在表的后端进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
顺序队列
在顺序队列中,为了降低运算的复杂度,元素入队时,只修改队尾指针;元素出对时,只修改队头指针。由于顺序队列的存储空间是提前设定的,因此队尾指针会有一个上限值,当队尾指针达到其上限时,就不能只通过修改队尾指针来实现新元素的入队操作了。
代码实现
//定义队列结构
type queue struct {
MaxSize int //定义队列容量
array [10]int
read int // 定义指向队尾
front int // 定义指向队头
}
//添加数据
func (queue *queue) add(data int) (err error) {
//判断队列是否已满
if queue.read == queue.MaxSize-1 {
return errors.New("队列满")
}
//添加数据,队尾下标+1
queue.read++
queue.array[queue.read] = data
return
}
//取数据
func (queue *queue) get() (data int, err error) {
if queue.read == queue.front {
return 0, errors.New("队列空")
}
//取数据队头下标+1
queue.front++
data = queue.array[queue.front]
return data, err
}
//展示数据
func (queue *queue) show() {
//获取数据从对头遍历到队尾
for i := queue.front + 1; i <= queue.read; i++ {
fmt.Println(i, queue.array[i])
}
}
func main() {
//初始化队列
q := &queue{
MaxSize: 10,
array: [10]int{},
read: -1,
front: -1,
}
var key string
var val int
for true {
fmt.Println("1 添加数据")
fmt.Println("2 获取数据")
fmt.Println("3 显示队列")
fmt.Println("4 退出")
_, err := fmt.Scanln(&key)
if err != nil {
return
}
switch key {
case "1":
_, err := fmt.Scanln(&val)
if err != nil {
return
}
err = q.add(val)
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println("成功")
}
case "2":
get, err := q.get()
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println(get)
}
case "3":
q.show()
case "4":
os.Exit(0)
}
}
}
假溢出
顺序队列入队和出队操作均是直接在其后面进行结点的链接和删除,这就造成其使用空间不断向出队的那一边偏移,产生假溢出。
假设是长度为5的数组,初始状态,空队列如所示,front与 rear指针均指向下标为-1的位置。
然后入队a1、a2、a3、a4, front指针依然指向下标为-1位置,而rear指针指向下标为3的位置。
出队a1、a2,则front指针指向下标为2的位置,rear不变
再入队a5,a6,此时front指针不变,rear指针移动到数组之外。
假设这个队列的总个数不超过5个,但目前如果接着入队的话,因数组末尾元素已经占用,再向后加,就会产生数组越界的错误,可实际上,我们的队列在下标为0和1的地方还是空闲的。我们把这种现象叫做假溢出
如何解决这个问题,能够利用到前面空闲的区域有两种方案
1、在元素出队列时所有元素向前平移,但是效率低下不建议采用
2、将新入队元素插入空闲区域,形成一个循环的结构,这就叫环形队列
环形队列
虽然使用循环队列,解决了假溢出问题,但是又有新问题发生——判空的问题,因为仅凭 front = rear 不能判定循环队列是空还是满。
空循环队列
满循环队列
这种情况下通常采用留一位元素空间的方式来进行判断,此时front = rear + 1
代码实现
//定义环形队列结构
type LoopQueue struct {
data [3]int //定义环形数组
head int //头部指针
tail int //尾部指针
cap int //环形数组大小
}
//入队列
func (LoopQueue *LoopQueue) add(val int) (err error) {
//判断队列是否已满
if (LoopQueue.tail+1)%LoopQueue.cap == LoopQueue.head {
return errors.New("队列满")
}
//放入数据
LoopQueue.data[LoopQueue.tail] = val
LoopQueue.tail = (LoopQueue.tail + 1) % LoopQueue.cap
return
}
//出队列
func (LoopQueue *LoopQueue) get() (val int, err error) {
//判断队列是否为空
if LoopQueue.head == LoopQueue.tail {
return 0, errors.New("队列空")
}
//赋值
val = LoopQueue.data[LoopQueue.head]
LoopQueue.head = (LoopQueue.head + 1) % LoopQueue.cap
return
}
//数据展示
func (LoopQueue *LoopQueue) show() {
//fmt.Println(LoopQueue.data)
length := LoopQueue.length()
if length == 0 {
fmt.Println("队列为空")
}
//设计辅助变量指向头部指针
tmpFront := LoopQueue.head
for i := 0; i < length; i++ {
fmt.Printf("data[%d]=%v\t", tmpFront, LoopQueue.data[tmpFront])
tmpFront = (tmpFront + 1) % LoopQueue.cap
}
fmt.Println()
}
//获取元素个数
func (LoopQueue *LoopQueue) length() int {
return (LoopQueue.tail + LoopQueue.cap - LoopQueue.head) % LoopQueue.cap
}
func main() {
q := LoopQueue{
data: [3]int{},
head: 0,
tail: 0,
cap: 3,
}
var key string
var val int
for true {
fmt.Println("1 添加数据")
fmt.Println("2 获取数据")
fmt.Println("3 显示队列")
fmt.Println("4 退出")
_, err := fmt.Scanln(&key)
if err != nil {
return
}
switch key {
case "1":
_, err := fmt.Scanln(&val)
if err != nil {
return
}
err = q.add(val)
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println("成功")
}
case "2":
get, err := q.get()
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println(get)
}
case "3":
q.show()
case "4":
os.Exit(0)
}
}
}