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)的更多相关文章

  1. python 下的数据结构与算法---2:大O符号与常用算法和数据结构的复杂度速查表

    目录: 一:大O记法 二:各函数高阶比较 三:常用算法和数据结构的复杂度速查表 四:常见的logn是怎么来的 一:大O记法 算法复杂度记法有很多种,其中最常用的就是Big O notation(大O记 ...

  2. 大数据学习之BigData常用算法和数据结构

    大数据学习之BigData常用算法和数据结构 1.Bloom Filter     由一个很长的二进制向量和一系列hash函数组成     优点:可以减少IO操作,省空间     缺点:不支持删除,有 ...

  3. TypeScript算法与数据结构-队列和循环队列

    本文涉及的源码,均在我的github.有两部分队列和循环队列.有问题的可以提个issue,看到后第一时间回复 1. 队列(Queue) 队列也是一种线性的数据结构, 队列是一种先进先出的数据结构.类似 ...

  4. PHP常用算法和数据结构示例

    <?php header("content-type:text/html;charset=utf-8"); $arr=array(3,5,8,4,9,6,1,7,2); ec ...

  5. php常用算法和数据结构

    </pre><pre name="code" class="php"><?php /** * Created by PhpStor ...

  6. python算法与数据结构-队列(44)

    一.队列的介绍 队列的定义:队列是一种特殊的线性表,只允许在表的头部(front处)进行删除操作,在表的尾部(rear处)进行插入操作的线性数据结构,这种结构就叫做队列.进行插入操作的一端称为队尾,进 ...

  7. Java数据结构与算法(4) - ch04队列(Queue和PriorityQ)

    队列: 先进先出(FIFO). 优先级队列: 在优先级队列中,数据项按照关键字的值有序,关键字最小的数据项总在对头,数据项插入的时候会按照顺序插入到合适的位置以确保队列的顺序,从后往前将小于插入项的数 ...

  8. 用golang实现常用算法与数据结构——跳跃表(Skip list)

    背景 最近在学习 redis,看到redis中使用 了skip list.在网上搜索了一下发现用 golang 实现的 skip list 寥寥无几,性能和并发性也不是特别好,于是决定自己造一个并发安 ...

  9. 数据结构 -- 队列Queue

    一.队列简介 定义 队列(queue)在计算机科学中,是一种先进先出的线性表. 它只允许在表的前端进行删除操作,而在表的后端进行插入操作.进行插入操作的端称为队尾,进行删除操作的端称为队头.队列中没有 ...

随机推荐

  1. java子类重写父类的要点

    子类不能重写父类的静态方法,私有方法.即使你看到子类中存在貌似是重写的父类的静态方法或者私有方法,编译是没有问题的,但那其实是你重新又定义的方法,不是重写.具体有关重写父类方法的规则如下:重写规则之一 ...

  2. 解析 Javascript - this

    在函数中 this 到底取何值,是在函数真正被调用执行的时候确定下来的,函数定义的时候确定不了.  因为 this 的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境.当你在 ...

  3. python开发:python基本数据类型

    运算符 1.算数运算: 2.比较运算: 3.赋值运算: 4.逻辑运算: 5.成员运算: 基本数据类型 1.数字 int(整型) 在32位机器上,整数的位数为32位,取值范围为-2**31-2**31- ...

  4. Hibernate(八):基于外键映射的1-1关联关系

    背景: 一个部门只有一个一把手,这在程序开发中就会设计数据映射应该设置为一对一关联. 在hibernate代码开发中,实现这个业务有两种方案: 1)基于外键映射的1-1关联: 2)基于主键映射的1-1 ...

  5. sumo快速运行简单仿真实例详细教程

    本文旨在让大家快速的了解sumo,并给出运行一个简单的sumo的例子的教程,进而了解基本sumo工程的架构,使大家对该软件产生兴趣并持续学习下去,刚开始学习仿真的确枯燥,项目"跑起来&quo ...

  6. [LeetCode] Valid Square 验证正方形

    Given the coordinates of four points in 2D space, return whether the four points could construct a s ...

  7. Linux服务器断电导致挂载及xfs文件损坏的修复方法

    系统文件损坏后进入紧急修复模式,无法进行维护工作 welcome to emergency mode!after logging in ,type "journalctl -xb" ...

  8. python文件结构与import用法

    首先上一张总结图: 在pycharm中,一般不会将当前文件目录自动加入自己的sourse_path.如果遇到无法import同级目录下的其他模块, 右键make_directory as-->S ...

  9. 利用 Scrapy 爬取知乎用户信息

    思路:通过获取知乎某个大V的关注列表和被关注列表,查看该大V和其关注用户和被关注用户的详细信息,然后通过层层递归调用,实现获取关注用户和被关注用户的关注列表和被关注列表,最终实现获取大量用户信息. 一 ...

  10. STL rope

    rope的部分简单操作 函数 功能 push_back(x) 在末尾添加x insert(pos,x) 在pos插入x erase(pos,x) 从pos开始删除x个 replace(pos,x) 从 ...