公众号:程序员波波
只是个人在开发中遇到一些小问题,可能没有解决得足够好,但是也是一次思考的过程。
场景
在开发的时候遇到一种情况,就是对于一系列的请求,我需要全部加入一个请求队列,然后通过统一的处理程序处理完毕后,放入结果队列。但是发出请求的程序,如果想从结果队列中取出自己请求的结果,需要一个全局唯一的ID,用以区分不同请求的结果。
一些尝试
对于这个问题,会有一些很直接的想法,比如:(1)用个count计数器,每次请求获得count这个ID,然后count加一;(2)对于每次请求,获取请求的当前时间,然后通过一些哈希,然后得到一个ID。
对于第一种想法
其实,就是事情固定了ID的一个范围或者说是取值,然后不断从这个范围内取数当做ID。
但是缺点就是count是无限往后加的,如果不考虑存储的局限,确实是全局唯一的。但是要想实现无限,需要更多的内存来保存这个ID值,得不偿失。
但是如果仅用一个表示范围有限的变量来存储,必然到后面会出现溢出,这样会与之前的ID冲突。
总结一下
就是ID不能有效的重复利用,因为ID只用于一次请求和结果,结束后应当释放此次的ID。
对于第二种想法
可能来说用时间哈希确实在一定程度是全局唯一的。但是哈希在数据量很大的时候,必然会出现冲突。
时间上来说要想区分不同时间,必然需要更精确的表示时间,在能精确表示的前提下,需要的内存将更多。
在有限的时间精度下,进行哈希,需要处理哈希冲突。这种情况下,可能需要维护哈希表,时间和空间上也就有了一定的要求。
总结一下
这种想法依赖于时间的精度或哈希,最大的问题还是冲突的问题。
尝试对第一种想法进行改进
第一种方法的缺点在于没有对ID进行重复利用。于是,我尝试对第一种想法进行改进,尝试对之前的ID进行维护。
数据结构的选取
想要维护历史释放的ID,必然需要通过一些数据结构进行维护。
链表?
其实count的活动空间就是一个线性空间,可以假想成一个数组。
在一系列ID释放的操作后,理论上这个数组中未被占用的元素可能是一段一段的。可以用链表维护这样一段一段的的范围。
节点元素:空闲区间。
初始状态:一个节点,0到最大值。
获取一个ID:遍历链表,得到第一个空闲区间,去掉区间中的第一个数作为ID。如果区间变成空区间,需要去掉这个节点。
释放一个ID:遍历链表,直到ID在两个相邻区间中间,插入这个数。如果相邻区间内合并,需要将区间合并成一个。
总结一下
时间复杂度上来说,获取和释放的最好情况都是O(1)的,但是最差可能变成线性的复杂度。
空间上来说,最好也是O(1),但是最差也可能变成线性的。
队列或栈?
与链表不同,队列或者栈是直接用来保存被释放的节点。
元素:被释放元素。
初始状态:count = 0, 队列或栈为空。
获取一个ID:先判断队列或者栈为空,不为空就直接从中获取,如果为空,就获取count,然后count加一。
释放一个ID:如果这个ID是count-1,就让count减一,否则就放入队列或者栈中。
总结一下
与链表不同的是,这种方法至少维护了一个count到最大值的连续区间,剩余所有的数,都保存在队列或者栈中。
时间复杂度上来说,获取和释放都是O(1)的。
空间复杂度上来说,很大程度上可能会成为线性的。
队列和栈的选取
假设所有请求的服务时间都是相同的话,而且是先来先服务,那么理论上用栈是更好的,因为这种情况下,第二次被获取的ID,释放顺序应该是大致倒序的,这样可以最大可能的等于count-1,这样就可以减少栈中的元素了。
当然队列应该也是有自身的好处的,本次不细研究队列和栈的优缺点,下面将以栈为例讨论。
代码的实现
下面的讨论,本次不考虑多线程情况。
首先因为ID是全局唯一的。所以需要实现单例模式。
类中的数据
class IdPool
{
protected:
static IdPool;
int count;
stack<int> rest;
};
IdPool *IdPool::idPool = NULL;
idPool一开始应该初始化为空指针。
构造函数
protected:
IdPool()
{
count = 0;
}
构造函数应该是private或者protected的,防止用户创建对象。
单例模式
public:
static IdPool *getIdPool()
{
if (idPool == NULL)
idPool = new IdPool();
return idPool;
}
用户通过getIdPool获得idPool对象。
获取ID
int getId()
{
if (rest.empty())
{
count++;
return count-1;
}
else
{
int id = rest.top();
rest.pop();
return id;
}
}
释放ID
void freeId(int id)
{
if (id == count-1) count--;
else rest.push(id);
}
未完待续…