redis-数据类型-链表
参考链接
redis的链表的是一个 无环双端链表
数据结构
链表节点结构
节点之前通过的,前后指针关联起来,构成双端链表,即可从头到尾遍历,也可从尾到头遍历。
typedef struct listNode {
struct listNode *prev;
struct listNode *next;
void *value;
} listNode;
链表整体结构
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;
重点函数
学习一下,双链表的操作
- 头插法 listAddNodeHead
list *listAddNodeHead(list *list, void *value)
{
listNode *node;
if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
node->value = value;
// 如果是首次插入的节点,则头尾都指向该节点
// 设置 node prev 和 next都为NULL
if (list->len == 0) {
list->head = list->tail = node;
node->prev = node->next = NULL;
} else {
node->prev = NULL; // 设置 新头结点的node->prev=NULL
node->next = list->head; // 新增node->next 指向 链表的头节点
list->head->prev = node; // 链表头结点list->head->prev = node(新增节点)
list->head = 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尾部节点->prev = list->tail 链表的尾部
node->next = NULL; // 新node尾部节点next =NULL
list->tail->next = node; // 链表尾部节点 next = 新node尾部节点
list->tail = node; // 重新设置 list->tail = node 尾部节点
}
list->len++;
return list;
}
- 插入到某个节点后面
list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
listNode *node;
if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
node->value = value;
if (after) { // 是否插入到 old_node的后面
node->prev = old_node; // 设置新 插入节点的指向关系
node->next = old_node->next;
if (list->tail == old_node) { // 如果 old_node是刚好是为尾部节点,则重新设置尾部节点
list->tail = node;
}
} else { // 插入到 old_node的 前面。
node->next = old_node; // 设置新插入节点指向关系
node->prev = old_node->prev;
if (list->head == old_node) { // 如果 old_node刚好头部节点,则重新设置 头部节点
list->head = node;
}
}
if (node->prev != NULL) { // 非头节点 则设置指向新的 新插入的节点
node->prev->next = node;
}
if (node->next != NULL) { // 非尾节点
node->next->prev = node;
}
list->len++;
return list;
}
- 删除 listDelNode
void listDelNode(list *list, listNode *node)
{
// 可能是头结点
if (node->prev) // 删除的前一个节点!=NULL
node->prev->next = node->next; // 删除节点的前一个节点的next = 要删除节点的下一个节点
else
list->head = node->next; // 删除的节点是头节点,则重新设置头节点,list->head = 删除节点的下一个节点
// 可能是尾节点
if (node->next) // 删除的下一个节点!=NULL
node->next->prev = node->prev; // 删除节点下一个节点的prev = 删除节点的prev
else
list->tail = node->prev; // 删除的节点是尾节点,则重新设置为尾节点,list->tail = 删除节点的前一个节点。
if (list->free) list->free(node->value);
zfree(node);
list->len--;
}
- 获取指定小标的元素 listIndex
listNode *listIndex(list *list, long index) {
listNode *n;
if (index < 0) {
index = (-index)-1; // 这样设计真的是 666,佩服
n = list->tail;
while(index-- && n) n = n->prev; // 当节点为NULL 或者 index为0 则返回
} else {
n = list->head;
while(index-- && n) n = n->next;
}
return n;
}
总结
- 无环双端链表:
链表节点有 前后指针,获取模个节点的前置节点和后置节点复杂度O(1)
链表 头节点prev 和 尾节点tail 都指向NULL,对链表的访问一NULL为终点。
- 链表长度
list结构内部维护一个 len的字段,所以获取长度为 O(1)复杂度
- 表头指针和表尾指针
list内部 有head 和tail 指针,分别指向链表的 头节点和 尾节点,则获取头尾节点的复杂度为O(1)