1、intset数据结构

整数集合intset用来实现集合键,当数据量小,并且都是整数的时候才使用,数据结构比较简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

typedef struct intset {

// 编码方式
uint32_t encoding;

// 集合包含的元素数量
uint32_t length;

// 保存元素的数组
int8_t contents[];

} intset;

intset 底层本质是一个有序的、不重复的、整型的数组,支持不同类型整数。

看一下查找算法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* Determine whether a value belongs to this set 
*
* 检查给定值 value 是否集合中的元素。
*
* 是返回 1 ,不是返回 0 。
*
* T = O(log N)
*/
uint8_t intsetFind(intset *is, int64_t value) {

// 计算 value 的编码
uint8_t valenc = _intsetValueEncoding(value);

// 如果 value 的编码大于集合的当前编码,那么 value 一定不存在于集合
// 当 value 的编码小于等于集合的当前编码时,
// 才再使用 intsetSearch 进行查找
return valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,NULL);
}

intsetSearch使用了二分查找算法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

/* Search for the position of "value".
*
* 在集合 is 的底层数组中查找值 value 所在的索引。
*
* Return 1 when the value was found and
* sets "pos" to the position of the value within the intset.
*
* 成功找到 value 时,函数返回 1 ,并将 *pos 的值设为 value 所在的索引。
*
* Return 0 when the value is not present in the intset
* and sets "pos" to the position where "value" can be inserted.
*
* 当在数组中没找到 value 时,返回 0 。
* 并将 *pos 的值设为 value 可以插入到数组中的位置。
*
* T = O(log N)
*/
static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {
int min = 0, max = intrev32ifbe(is->length)-1, mid = -1;
int64_t cur = -1;

/* The value can never be found when the set is empty */
// 处理 is 为空时的情况
if (intrev32ifbe(is->length) == 0) {
if (pos) *pos = 0;
return 0;
} else {
/* Check for the case where we know we cannot find the value,
* but do know the insert position. */
// 因为底层数组是有序的,如果 value 比数组中最后一个值都要大
// 那么 value 肯定不存在于集合中,
// 并且应该将 value 添加到底层数组的最末端
if (value > _intsetGet(is,intrev32ifbe(is->length)-1)) {
if (pos) *pos = intrev32ifbe(is->length);
return 0;
// 因为底层数组是有序的,如果 value 比数组中最前一个值都要小
// 那么 value 肯定不存在于集合中,
// 并且应该将它添加到底层数组的最前端
} else if (value < _intsetGet(is,0)) {
if (pos) *pos = 0;
return 0;
}
}

// 在有序数组中进行二分查找
// T = O(log N)
while(max >= min) {
mid = (min+max)/2;
cur = _intsetGet(is,mid);
if (value > cur) {
min = mid+1;
} else if (value < cur) {
max = mid-1;
} else {
break;
}
}

// 检查是否已经找到了 value
if (value == cur) {
if (pos) *pos = mid;
return 1;
} else {
if (pos) *pos = min;
return 0;
}
}

二分查找算法用到的地方还真是多,以后是应该详细学习一下常用的算法了。

我没有看intset的插入,是因为我对于c语言忘得差不多了,其中涉及到类型升级,看着晕。
但是原理应该都差不多,找到位置插入,这个找到插入位置也使用了上面的intsetSearch函数的*pos。

tencent.jpg