链表提供了高效的节点重拍能力,以及顺序性的节点访问方式,并且可以通过增删节点的方式来灵活地调整链表的长度。链表在Redis中应用十分广泛,比如列表的底层实现就是链表。
链表及节点的实现
链表的每个节点表示如下(adlist.h):
typedef struct listNode {
//前驱
struct listNode *prev;
//后继
struct listNode *next;
//节点的值
void *value;
} listNode;
不难发现,Redis的链表是个双向链表。
使用一个list结构来持有链表:
typedef struct list {
//表头
listNode *head;
//表尾
listNode *tail;
//链表节点个数
unsigned long len;
//复制节点值函数
void *(*dup)(void *ptr);
//节点值释放函数
void (*free)(void *ptr);
//节点值匹配函数
int (*match)(void *ptr, void *key);
} list;
Redis链表实现特性总结:
- 双向:链表节点带有prev和next指针,获取某个节点的前置节点和后置节点的复杂度为O(1)
- 无环:表头节点的prev指针和表尾节点的next指针都指向NULL,对链表的访问以NULL为终点
- 带头指针和尾指针:通过list结构的head指针和tail指针,程序获取链表的表头节点和表尾及诶单的时间复杂度为O(1)
- 带链表长度计数器:程序使用list结构的len属性来对list持有的链表节点进行计数,程序获取链表中节点数量的复杂度为O(1)
- 多态:链表节点使用void* 指针来保存节点值,并且可以通过list结构的dup、free、match三个属性为节点值设置类型特定的函数,所以链表可以用于保存各种不同类型的值
API
listRelease
//释放整个链表空间
void listRelease(list *list)
{
unsigned long len;
listNode *current, *next;
current = list->head;
len = list->len;
while(len--) {
next = current->next;
if (list->free) list->free(current->value);
zfree(current);
current = next;
}
//它是遍历整个链表,释放一个个节点后再释放list的
zfree(list);
}
listAddNodeHead
头插法插入链表
list *listAddNodeHead(list *list, void *value)
{
listNode *node;
if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
node->value = value;
if (list->len == 0) {
list->head = list->tail = node;
node->prev = node->next = NULL;
} else {
node->prev = NULL;
node->next = list->head; #从头部插入
list->head->prev = node;
list->head = node;
}
list->len++;
return list;
}
listAddNodeTail
尾插法插入链表
list *listAddNodeTail(list *list, void *value)
{
listNode *node;
if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
node->value = value;
if (list->len == 0) {
list->head = list->tail = node;
node->prev = node->next = NULL;
} else {
node->prev = list->tail; # 从尾部插入
node->next = NULL;
list->tail->next = node;
list->tail = node;
}
list->len++;
return list;
}
list的API实现还是很简单的,稍微开一下就行,知道大概思路就差不多了
版权属于:带翅膀的猫
本文链接:https://www.chengpengper.cn/archives/108/
转载时须注明出处及本声明