用go实现常用算法与数据结构——队列(queue)
queue 简介
队列是一种非常常见的数据结构,日常生活中也能经常看到。一个典型的队列如下图(图片来自 segmentfault):
可以看出队列和我们日常生活中排队是基本一致的。都遵循 FIFO(First In First Out)的原则。
实现
队列可以使用链表或者数组实现,使用链表的优点是扩容简单,缺点是无法通过索引定位元素,使用数组则相反,扩容不容易但是可以通过索引定位元素。文章采用双向链表实现。代码放在github:
https://github.com/AceDarkknight/AlgorithmAndDataStructure/tree/master/queue
链表一般有下面这几个基本操作,先定义一个接口,方便开发和测试:
type Queue interface {
// 获取当前链表长度。
Length() int
// 获取当前链表容量。
Capacity() int
// 获取当前链表头结点。
Front() *Node
// 获取当前链表尾结点。
Rear() *Node
// 入列。
Enqueue(value interface{}) bool
// 出列。
Dequeue() interface{}
}
笔者的实现中,front 和 rear 节点不保存具体值,只是用来指示真正头尾节点的位置。
链表实现的队列
入列的实现如下:
// normalQueue.go
func (q *NormalQueue) Enqueue(value interface{}) bool {
if q.length == q.capacity || value == nil {
return false
}
node := &Node{
value: value,
}
if q.length == 0 {
q.front.next = node
}
node.previous = q.rear.previous
node.next = q.rear
q.rear.previous.next = node
q.rear.previous = node
q.length++
return true
}
出列的实现:
// normalQueue.go
func (q *NormalQueue) Dequeue() interface{} {
if q.length == 0 {
return nil
}
result := q.front.next
q.front.next = result.next
result.next = nil
result.previous = nil
q.length--
return result.value
}
可以看到,具体实现和链表基本一致,这种方法好处在于不需要考虑数组溢出的问题。但是有时候,我们可能会向 queue 插入相同的元素,我们当前的实现是无法判断数据是否已经存在的,这时我们就需要实现一个无重复元素的 queue。
无重复元素的队列。
我们只需要在原来的基础上加一个 Map 存放我们的具体值就可以了。直接上代码:
// uniqueQueue.go
func (q *UniqueQueue) Enqueue(value interface{}) bool {
if q.length == q.capacity || value == nil {
return false
}
node := &Node{
value: value,
}
// Ignore uncomparable type.
if kind := reflect.TypeOf(value).Kind(); kind == reflect.Map || kind == reflect.Slice || kind == reflect.Func {
return false
}
if v, ok := q.nodeMap[value]; ok || v {
return false
}
if q.length == 0 {
q.front.next = node
}
node.previous = q.rear.previous
node.next = q.rear
q.rear.previous.next = node
q.rear.previous = node
q.nodeMap[value] = true
q.length++
return true
}
因为在 golang 中,map 的 key 必须是可以比较的,所以我们需要排除 Map、slice、function 这些不可比较的类型。剩下的实现和上面的就差不多了。再看出列操作:
// uniqueQueue.go
func (q *UniqueQueue) Dequeue() interface{} {
if q.length == 0 {
return nil
}
result := q.front.next
delete(q.nodeMap, result.value)
q.front.next = result.next
result.next = nil
result.previous = nil
q.length--
return result.value
}
上面两个队列都是基于链表实现的,下面介绍一下基于数组实现的循环队列。
循环队列
循环队列通过复用数组元素来达到“循环”的效果。简单来说就是如果数组前面有位置,就把元素放进去。具体原理可以看这里。入列代码如下:
// cyclicQueue.go
func (q *CyclicQueue) Enqueue(value interface{}) bool {
if q.length == q.capacity || value == nil {
return false
}
node := &Node{
value: value,
}
index := (q.rear + 1) % cap(q.nodes)
q.nodes[index] = node
q.rear = index
q.length++
if q.length == 1 {
q.front = index
}
return true
}
出列操作也类似:
// cyclicQueue.go
func (q *CyclicQueue) Dequeue() interface{} {
if q.length == 0 {
return nil
}
result := q.nodes[q.front].value
q.nodes[q.front] = nil
index := (q.front + 1) % cap(q.nodes)
q.front = index
q.length--
return result
}
Reference
https://www.geeksforgeeks.org/queue-set-1introduction-and-array-implementation/
用go实现常用算法与数据结构——队列(queue)的更多相关文章
- python 下的数据结构与算法---2:大O符号与常用算法和数据结构的复杂度速查表
目录: 一:大O记法 二:各函数高阶比较 三:常用算法和数据结构的复杂度速查表 四:常见的logn是怎么来的 一:大O记法 算法复杂度记法有很多种,其中最常用的就是Big O notation(大O记 ...
- 大数据学习之BigData常用算法和数据结构
大数据学习之BigData常用算法和数据结构 1.Bloom Filter 由一个很长的二进制向量和一系列hash函数组成 优点:可以减少IO操作,省空间 缺点:不支持删除,有 ...
- TypeScript算法与数据结构-队列和循环队列
本文涉及的源码,均在我的github.有两部分队列和循环队列.有问题的可以提个issue,看到后第一时间回复 1. 队列(Queue) 队列也是一种线性的数据结构, 队列是一种先进先出的数据结构.类似 ...
- PHP常用算法和数据结构示例
<?php header("content-type:text/html;charset=utf-8"); $arr=array(3,5,8,4,9,6,1,7,2); ec ...
- php常用算法和数据结构
</pre><pre name="code" class="php"><?php /** * Created by PhpStor ...
- python算法与数据结构-队列(44)
一.队列的介绍 队列的定义:队列是一种特殊的线性表,只允许在表的头部(front处)进行删除操作,在表的尾部(rear处)进行插入操作的线性数据结构,这种结构就叫做队列.进行插入操作的一端称为队尾,进 ...
- Java数据结构与算法(4) - ch04队列(Queue和PriorityQ)
队列: 先进先出(FIFO). 优先级队列: 在优先级队列中,数据项按照关键字的值有序,关键字最小的数据项总在对头,数据项插入的时候会按照顺序插入到合适的位置以确保队列的顺序,从后往前将小于插入项的数 ...
- 用golang实现常用算法与数据结构——跳跃表(Skip list)
背景 最近在学习 redis,看到redis中使用 了skip list.在网上搜索了一下发现用 golang 实现的 skip list 寥寥无几,性能和并发性也不是特别好,于是决定自己造一个并发安 ...
- 数据结构 -- 队列Queue
一.队列简介 定义 队列(queue)在计算机科学中,是一种先进先出的线性表. 它只允许在表的前端进行删除操作,而在表的后端进行插入操作.进行插入操作的端称为队尾,进行删除操作的端称为队头.队列中没有 ...
随机推荐
- oracle获取表字段属性
select b.COMMENTS,a.COLUMN_NAME,a.DATA_TYPE,a.DATA_LENGTH, a.DATA_PRECISION,a.DATA_SCALE,a.NULLABLE, ...
- Centos7 安装python3
Centos7 安装python3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #安装sqlite-devel yum -y ...
- jacascript JSON对象的学习
前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! JSON (javascript object notation) 全称是 javascript 对象表示 ...
- JProfiler简明使用教程
JProfile是一款性能瓶颈分析工具,监控粒度可以细化到某一个类包,堪称神器!我安装了一下9.11的版本,并简单说说使用方法. 1:创建一个监控任务 2:选择tomcat版本 3:监控远程服务器 4 ...
- Java中List集合的三种遍历方式(全网最详)
List集合在Java日常开发中是必不可少的,只要懂得运用各种各样的方法就可以大大提高我们开发的效率,适当活用各种方法才会使我们开发事半功倍. 我总结了三种List集合的遍历方式,下面一一来介绍. 首 ...
- nginx location的命中过程
1 先判断精准命中,立即返回结果并结束解析过程 2 判断普通命中,如果有多个命中,"记录"下"最长"的命中结果(注意:记录但不结束,最长的为准) 3 继续判断正 ...
- 热力图heatmap.js使用中的思路解析
官网: https://www.patrick-wied.at/static/heatmapjs/ 需求:使用heatmap.js制作热力图,反映人群分布情况 问题:热力图需要的数据:坐标 + 人数 ...
- [COGS 1799][国家集训队2012]tree(伍一鸣)
Description 一棵n个点的树,每个点的初始权值为1.对于这棵树有q个操作,每个操作为以下四种操作之一: + u v c:将u到v的路径上的点的权值都加上自然数c: - u1 v1 u2 v2 ...
- [AHOI2016初中组]迷宫
题目描述 小雪和小可可被困在了一个无限大的迷宫中. 已经知道这个迷宫有 N 堵环状的墙,如果把整个迷宫看作是一个二维平面,那么每一堵墙都是平面上一个圆.任意两个圆不相交,不重合,也不会相切, 但有可能 ...
- ●BZOJ 1853 [Scoi2010]幸运数字
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=1853 题解: 容斥原理,暴力搜索,剪枝(这剪枝剪得真玄学) 首先容易发现,幸运号码不超过 2 ...