队列原理解析及Go实现

顺序队列
循环队列

队列(Queue)与栈都是线性存储结构,因此以常见的线性表如数组、链表作为底层的数据结构,特点:

  • 先进先出(First In First Out)的原则,简称FIFO结构
  • 队尾添加元素,队首删除元素

相关概念:

  • 队头与队尾: 允许元素插入的一端称为队尾,允许元素删除的一端称为队头
  • 入队:队列的插入操作
  • 出队:队列的删除操作
    在这里插入图片描述
    在这里插入图片描述

顺序队列

顺序存储上的不足:每次从数组头部删除元素(出队)后,需要将头部以后的所有元素往前移动一个位置,这是一个时间复杂度为O(n)的操作:
在这里插入图片描述
可能有人说,把队首标志往后移动不就不用移动元素了吗?但那样会造成数组空间的“流失”。我们希望队列的插入与删除操作都是O(1)的时间复杂度,同时不会造成数组空间的浪费,我们应该使用循环队列

基于数组实现顺序队列 代码
package main

import (
	"errors"
	"fmt"
	"os"
)

// 使用结构体管理队列
type Queue struct {
	Maxsize int
	array   [5]int //数组 模拟队列
	front   int    // 指向队列首部
	real    int    // 指向队列 尾部
}

// 入队
func (this *Queue) Push(val int) (err error) {
	// 判断队列是否满
	if this.real == this.Maxsize-1 {
		return errors.New("queue full")
	}
	this.real++
	this.array[this.real] = val
	return
}

// 显示队列
func (this *Queue) showQueue() {
	for i := this.front + 1; i <= this.real; i++ {
		fmt.Printf("%d\t", this.array[i])
	}
	fmt.Println()
}

// 出队
func (this *Queue) Pop() (val int, err error) {

	if this.front == this.real {
		return val, errors.New("queue empty")
	}
	this.front++
	val = this.array[this.front]
	return
}
func main() {
	queue := &Queue{
		Maxsize: 5,
		front:   -1,
		real:    -1,
	}
	var key string
	var val int
	for {
		fmt.Printf("1、输入push 入队\n2、输入pop 出队\n3、输入show 显示队列\n4、输入eixt 表示退出\n")
		fmt.Scanln(&key)

		switch key {
		case "push":
			fmt.Println("请输入你要入队的数据:")
			fmt.Scanln(&val)
			err := queue.Push(val)
			if err != nil {
				fmt.Println(err)
			} else {
				fmt.Println("入队成功")
			}
		case "pop":
			val, err := queue.Pop()
			if err != nil {
				fmt.Println(err)
			} else {
				fmt.Println("出队数值",val)
			}
		case "show":
			queue.showQueue()
		case "exit":
			os.Exit(-1)

		}
	}
}

循环队列

它可以把数组看出一个首尾相连的圆环,删除元素时将队首标志往后移动,添加元素时若数组尾部已经没有空间,则考虑数组头部的空间是否空闲,如果是,则在数组头部进行插入
在这里插入图片描述

循坏队列的核心在队头指针和队尾指针的增加方式:

this.tail=(this.tail+1)%this.maxsize
this.head=(this.head+1)%this.

当队列为空时this.head = this.tail,当队列为满时也有this.head = this.tail造成了判断队列是空还是满的二义性

解决方法:
1.增加一个参数,使删除元素为1,插入元素为0
2.增加一个参数,记录队列的元素个数即长度
3.空出一个单元,令(this.tail+1)%this.maxsize == this.head为队列为满的条件,以此解决二义性

基于数组实现循坏队列 代码
package main

import (
	"errors"
	"fmt"
	"os"
)

// 使用结构体管理队列
type CycleQueue struct {
	maxsize int
	array   [5]int //数组
	head    int    //队首
	tail    int    //队尾
}

// 入队列
func (this *CycleQueue) Push(val int) error {
	if this.IsFull() {
		return errors.New("queue full")
	}
	this.array[this.tail] = val
	this.tail=(this.tail+1)%this.maxsize
	return nil
}

//出队列
func (this *CycleQueue) Pop() (val int, err error) {
	if this.IsEmpty() {
		return val, errors.New("queue empty")
	}
	val = this.array[this.head]
	this.head=(this.head+1)%this.maxsize
	return val, nil
}

//显示队列
func (this *CycleQueue) ShowQ() {
	size := this.Size()
	if size == 0 {
		fmt.Println("queue is empty")
	}
	//辅助变量 指向head
	tempHead := this.head
	for i := 0; i < size; i++ {
		fmt.Println(this.array[tempHead])
		tempHead = (tempHead + 1) % this.maxsize
	}
}

//判断循环队列是否为空
func (this *CycleQueue) IsEmpty() bool {
	return this.tail == this.head
}

//判断循环队列是否满
func (this *CycleQueue) IsFull() bool {
	return (this.tail+1)%this.maxsize == this.head
}

//统计队列元素 个数
func (this *CycleQueue) Size() int {
	return (this.tail + this.maxsize - this.head) % this.maxsize
}

func main() {
	queue := &CycleQueue{
		maxsize: 5,
		tail:    0,
		head:    0,
	}
	var key string
	var val int
	for {
		fmt.Printf("1、输入push 入队\n2、输入pop 出队\n3、输入show 显示队列\n4、输入eixt 表示退出\n")
		fmt.Scanln(&key)

		switch key {
		case "push":
			fmt.Println("请输入你要入队的数据:")
			fmt.Scanln(&val)
			err := queue.Push(val)
			if err != nil {
				fmt.Println(err)
			} else {
				fmt.Println("入队成功")
			}
		case "pop":
			val, err := queue.Pop()
			if err != nil {
				fmt.Println(err)
			} else {
				fmt.Println("出队数值", val)
			}
		case "show":
			queue.ShowQ()
		case "exit":
			os.Exit(-1)

		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值