golang实现循环队列的示例代码

 更新时间:2024年07月02日 09:57:51   作者:littleboy_webrtc  
循环队列是一种使用固定大小的数组来实现队列的数据结构,本文主要介绍了golang实现循环队列的示例代码,具有一定的参考价值,感兴趣的可以了解一下

概要说明

循环队列是一种使用固定大小的数组来实现队列的数据结构,它通过循环的方式使用数组空间,具有以下好处:

  • 空间高效:循环队列避免了使用链表实现队列时可能存在的额外空间开销,因为链表的每个节点都需要额外的存储空间来保存指向下一个节点的指针。
  • 时间效率:在循环队列中,入队和出队操作通常可以在常数时间内完成,即O(1)时间复杂度。这是因为不需要移动队列中的其他元素。
  • 减少内存分配:由于循环队列使用固定大小的数组,它避免了动态分配内存的需要,这在某些情况下可以减少内存分配和回收的开销。
  • 避免内存碎片:固定大小的数组可以减少内存碎片的产生,因为不需要为每个元素单独分配内存。
  • 实现简单:循环队列的实现相对简单,只需要管理两个指针(或索引)来跟踪队列的头部和尾部。
  • 动态使用:循环队列可以动态地使用数组空间,即使队列满了,也可以通过循环的方式继续使用数组的未使用部分。
  • 适用于固定大小数据集:当数据集的大小已知且固定时,循环队列可以非常高效地使用内存和处理数据。
  • 减少错误:由于循环队列的实现相对简单,它减少了因复杂实现带来的潜在错误。

避免假溢出是循环队列设计中的一个重要问题。假溢出通常发生在循环队列的头部和尾部索引在数组中相遇时,即使数组中还有空间可以存放新元素。出现假溢出的原因,从根本上来说,是因为循环队列的容量被错误地判断为已满。

队列常见问题:假溢出

以下是几种避免假溢出的策略:

  • 使用标志位:在循环队列结构中添加一个标志位来区分队列是真正的满还是假溢出。当队列满时,设置这个标志位;当队列空时,清除这个标志位。
  • 使用计数器:维护一个计数器来记录队列中实际的元素数量。入队时增加计数器,出队时减少计数器。即使头部和尾部索引相遇,计数器也能正确反映队列的状态。
  • 双重索引:使用两个索引来分别表示队列的头部和尾部,同时使用一个布尔值来区分队列是满还是空。当队列满时,尾部索引会等于头部索引,但布尔值会标记队列为满。
  • 重新计算索引:在检查队列是否满或空时,不要仅仅依赖于头部和尾部索引的比较,而是重新计算它们在数组中的相对位置。
  • 使用额外的数组空间:在数组中预留一个额外的空间,这样即使头部和尾部索引相遇,也不会立即认为队列满了。这种方式牺牲了一点空间效率,但可以简化逻辑。
  • 使用环形缓冲区:在某些编程语言的库中,环形缓冲区(ring buffer)是一种特殊的循环队列,它通过使用上述一些策略来避免假溢出。
  • 动态扩容:虽然这并不直接解决假溢出问题,但通过动态扩容,可以在队列达到容量上限时创建一个新的更大的数组,并将旧数组中的元素复制到新数组中。
  • 使用哨兵元素:在数组的末尾添加一个哨兵元素,这样即使头部和尾部索引相遇,哨兵元素也不会被误认为是队列中的有效元素。
  • 逻辑分离:在逻辑上将满和空的状态与头部和尾部索引的相遇分离,通过额外的逻辑来确定队列的真实状态。
  • 使用状态位图:使用位图来表示数组中哪些位置是被占用的,这样可以通过位图的状态来判断队列是否满或空。

避免假溢出的关键在于正确地维护队列的状态信息,确保在任何时候都能准确地判断队列是否真正满了或者空了。在设计循环队列时,应该根据具体的应用场景和性能要求来选择最合适的策略。

在Go语言中实现循环队列,我们需要定义一个循环队列的数据结构,并实现其基本操作,包括初始化、入队(Push)、出队(Pop)、获取队列长度等。下面是一个简单的循环队列实现示例:

循环队列的实现

package QUEUE

import (
	"strings"
	"sync"
)

type CycleQueue struct {
	data  []interface{} //存储空间
	front int           //前指针,前指针负责弹出数据移动
	rear  int           //尾指针,后指针负责添加数据移动
	cap   int           //设置切片最大容量
	mu    sync.RWMutex          // 添加互斥锁
}

func NewCycleQueue(cap int) *CycleQueue {
	return &CycleQueue{
		data:  make([]interface{}, cap),
		cap:   cap,
		front: 0,
		rear:  0,
	}
}

// 入队操作
// 判断队列是否队满,队满则不允许添加数据
func (q *CycleQueue) Push(data interface{}) bool {
	q.mu.Lock()
	defer q.mu.Unlock() // 确保在函数退出时释放锁

	//check queue is full
	if q.QueueLength() == q.cap { //队列已满时,不执行入队操作
		Log.Error("push err queue is full")
		return false
	}
	q.data[q.rear] = data         //将元素放入队列尾部
	q.rear = (q.rear + 1) % q.cap //尾部元素指向下一个空间位置,取模运算保证了索引不越界(余数一定小于除数)
	return true
}

// 出队操作
// 需要考虑: 队队为空没有数据返回了
func (q *CycleQueue) Pop() interface{} {
	q.mu.Lock()
	defer q.mu.Unlock()

	if q.rear == q.front {
		return nil
	}

	data := q.data[q.front]
	q.data[q.front] = nil // 使用nil来避免内存泄露
	q.front = (q.front + 1) % q.cap
	return data
}

// 循环队列长度计算,考虑哨兵空间
func (q *CycleQueue) QueueLength() int {
	if q.rear >= q.front {
		return q.rear - q.front
	}
	return q.cap - q.front + q.rear
}

func (q *CycleQueue) FindDataByRequestId(requestId string) interface{} {
	q.mu.Lock()
	defer q.mu.Unlock()

	// 计算队列中的有效元素数量
	length := q.QueueLength()

	// 从front指针开始遍历队列
	index := q.front
	for i := 0; i < length; i++ {
		// 检查当前元素是否包含requestId
		if data, ok := q.data[index].(string); ok && strings.Contains(data, requestId) {
			// 找到元素后,从队列中移除
			q.RemoveAtIndex(index)
			return data
		}
		// 移动到下一个元素
		index = (index + 1) % q.cap
	}
	// 如果没有找到,返回nil
	return nil
}

// 新增RemoveAtIndex方法,用于在循环队列中移除指定索引的元素
func (q *CycleQueue) RemoveAtIndex(index int) {
	if index < 0 || index >= q.cap {
		return // 索引越界检查
	}

	// 将index位置的元素复制到最后一个元素的位置
	copy(q.data[index:], q.data[index+1:q.rear])

	// 更新队列的front和rear指针
	if index == q.rear { // 如果移除的是最后一个元素
		q.rear = index // rear指针向前移动
	} else if index < q.rear { // 如果移除的是中间的元素
		q.rear-- // rear指针向前移动
	}

	// 如果移除的是front指针前的元素
	if index < q.front {
		q.front = index // front指针向后移动
	}
}

循环队列的应用

package server

import (
    "encoding/json"
    "strconv"
    "time"
)

//队列通道
type Channel struct {
    queue                  *MeetGo.CycleQueue
}

//队列参数项
type QueueItem struct {
    event    string
    msg      map[string]interface{}
    response *chan Response
}

//返回参数
type Response struct {
	err    interface{}
	result map[string]interface{}
}

var globalChannel *Channel


//创建队列通道
func NewChannel() {
    channel.queue = QUEUE.NewCycleQueue(1000)
    go channel.handle()
    return
}

//处理队列数据
func (channel *Channel) handle() {
    for {
        item := channel.queue.Pop()

        if item == nil {
            time.Sleep(40 * time.Millisecond)
            continue
        }
        channel.process(item)
        time.Sleep(1 * time.Millisecond)
    }
}

func (channel *Channel) process(inter interface{}) {
    item := inter.(QueueItem)
}

func (channel *Channel) PushSignal(item QueueItem) {
    Channel.queue.Push(item)
}

func (channel *Channel) onRequest(event string, msg map[string]interface{}) chan Response {
	response := make(chan Response, 1)
	item := QueueItem{
		event,
		msg,
		&response,
	}
	channel.PushSignal(item)
	return response

}

func main(){
    globalChannel = NewChannel()
    globalChannel.onRequset()
    println(response)
}

到此这篇关于golang实现循环队列的示例代码的文章就介绍到这了,更多相关golang 循环队列内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • Go 库性能分析工具pprof

    Go 库性能分析工具pprof

    这篇文章主要为大家介绍了Go 库性能分析工具pprof,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • goland 清除所有的默认设置操作

    goland 清除所有的默认设置操作

    这篇文章主要介绍了goland 清除所有的默认设置操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • 基于Go+OpenCV实现人脸识别功能的详细示例

    基于Go+OpenCV实现人脸识别功能的详细示例

    OpenCV是一个强大的计算机视觉库,提供了丰富的图像处理和计算机视觉算法,本文将向你介绍在Mac上安装OpenCV的步骤,并演示如何使用Go的OpenCV绑定库进行人脸识别,需要的朋友可以参考下
    2023-07-07
  • Go语言实战学习之流程控制详解

    Go语言实战学习之流程控制详解

    这篇文章主要为大家详细介绍了Go语言中的流程控制,文中的示例代码讲解详细,对我们学习Go语言有一定的帮助 ,需要的朋友可以参考下
    2022-08-08
  • Go扩展原语之SingleFlight的用法详解

    Go扩展原语之SingleFlight的用法详解

    Go语言扩展包同步原语singleflight.Group能够再一个服务中抑制对下游的多次重复请求,它能够限制对同一个键值对的多次重复请求,减少对下游的瞬时流量,接下来小编就给大家讲讲Go SingleFlight的具体用法,需要的朋友可以参考下
    2023-07-07
  • Go语言与其他语言进行交互的方式详解

    Go语言与其他语言进行交互的方式详解

    在当今的软件开发领域,多种编程语言常常需要协同工作,以充分利用各自的优势来构建复杂的应用系统,Go 语言作为一门高效、简洁的编程语言,也经常需要与其他语言进行交互,接下来,我们将详细探讨 Go 语言如何与其他语言进行交互,需要的朋友可以参考下
    2024-06-06
  • golang实现简单工厂、方法工厂、抽象工厂三种设计模式

    golang实现简单工厂、方法工厂、抽象工厂三种设计模式

    这篇文章介绍了golang实现简单工厂、方法工厂、抽象工厂三种设计模式的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • 深入理解golang中io.Writer接口的使用

    深入理解golang中io.Writer接口的使用

    io 是一个 Golang 标准库包,它为围绕输入和输出的许多操作和用例定义了灵活的接口,这篇文章主要为大家介绍了Go中Writer接口的使用,需要的可以参考下
    2023-10-10
  • Golang的Crypto/SHA256库实战指南

    Golang的Crypto/SHA256库实战指南

    无论是在保护数据安全、验证数据完整性,还是在构建复杂的安全系统中,crypto/sha256都是Golang程序员不可或缺的工具,本文主要介绍了Golang的Crypto/SHA256库实战指南,感兴趣的可以了解一下
    2024-02-02
  • 入门学习Go的基本语法

    入门学习Go的基本语法

    这篇文章主要介绍了Go语法的基本概念,本文讲解了Golang的基本知识、基础语法、相关术语等,需要的朋友可以参考下,希望能帮助到你
    2021-07-07

最新评论