程序员PHP经验分享

队列、堆栈和优先队列介绍及Redis实现

2018-12-01  本文已影响15人  solohunter

前言

队列、堆栈和优先队列是编程中常见的数据结构。本文首先简单介绍一下这几种数据结构,然后介绍如何用Redis实现这些数据结构。

数据结构简介

队列

普通队列有以下几个特性

从普通队列可以衍生出定长队列,它比普通队列多出以下特性

从定长队列可以衍生出可溢出的定长队列,它比定长队列多出以下特性

以上三种队列都是单向队列,只能从尾端PUSH,从前端POP。它们又分别可以衍生出各自的双向版本。

双向 队列/定长队列/可溢出的定长队列

比单向版本多处以下特性

堆栈

普通堆栈有以下特性

衍生出的定长堆栈多处以下特性

定长堆栈没有可溢出版本,因为堆栈只能从尾端PUSH/POP。

优先队列

和普通队列相比,优先队列中的元素都有一个优先级,队列中的元素按优先级排序,优先级高的元素先被弹出。类似队列,优先队列有三种版本,他们的特性也类似队列。

其中可溢出的优先队列,满载时的PUSH操作会将优先级低的元素挤出。

Redis实现

思路

队列和堆栈都可以用Redis的list数据类型实现,因为list支持以下操作

定长和可溢出版本,Redis原生的list类型无法实现,需要写一点代码实现它们。Redis支持Lua脚本编程,我们就用它来实现这些版本。

优先队列可以用Redis的ZSET(SORTED SET)数据类型实现,ZSET为有序集合,集合中的元素都有一个分值,分值越低优先级越高。我们同样用Lua脚本实现定长和可溢出版本。ZSET支持以下操作

实现细节

队列

rpush + lpop即可实现一个普通队列。

对于定长队列,push前需检查队列长度,如果满载则返回错误码,否则追加元素。

对于可溢出的定长队列,push后检查队列长度,如果长度超出容量,则从前端将元素弹出。

堆栈双向队列的实现细节与之类似。

优先队列

增强特性

为了充分利用Redis的强大之处,我们还可以增加一些有趣的特性

代码实例

我们看一下如何用Lua脚本实现可溢出的定长队列

PUSH

local cap=tonumber(ARGV[1])
for i,k in ipairs(ARGV) do
  if i > 1 then
    redis.call('rpush',KEYS[1],k)
  end
end
local len=redis.call('llen',KEYS[1])
local o={}
while len > cap do
  o[#o + 1]=redis.call('lpop',KEYS[1])
  len=len - 1
end
return { len,o }

POP

local o={}
for i=1,tonumber(ARGV[1]) do
  o[#o + 1]=redis.call('lpop',KEYS[1])
end
return o

代码已开源至github

Python版本

PHP版本

上一篇下一篇

猜你喜欢

热点阅读