链表提供了高效的节点重拍能力,以及顺序性的节点访问方式,并且可以通过增删节点的方式来灵活地调整链表的长度。链表在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实现还是很简单的,稍微开一下就行,知道大概思路就差不多了

Last modification:September 11th, 2020 at 02:10 pm
如果觉得我的文章对你有用,请随意赞赏