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)
        }
    }
}
posted @ 2021-04-15 10:13  冰乐  阅读(2000)  评论(0编辑  收藏  举报