zoukankan      html  css  js  c++  java
  • redis源码学习_链表

    redis的链表是双向链表,该链表不带头结点,具体如下:

    主要总结一下adlist.c和adlist.h里面的关键结构体和函数。

    链表节点结构如下:

     1 /*
     2  * 双端链表节点
     3  */
     4 typedef struct listNode {
     5 
     6     // 前置节点
     7     struct listNode *prev; //如果是list的头结点,则prev指向NULL
     8 
     9     // 后置节点
    10     struct listNode *next;//如果是list尾部结点,则next指向NULL
    11 
    12     // 节点的值
    13     void *value;
    14 
    15 } listNode;

    链表结构如下:

     1 /*
     2  * 双端链表结构
     3  */
     4 typedef struct list {
     5 
     6     // 表头节点
     7     listNode *head;
     8 
     9     // 表尾节点
    10     listNode *tail;
    11 
    12     // 节点值复制函数
    13     void *(*dup)(void *ptr);
    14 
    15     // 节点值释放函数
    16     void (*free)(void *ptr);
    17 
    18     // 节点值对比函数
    19     int (*match)(void *ptr, void *key);
    20 
    21     // 链表所包含的节点数量,有了这个我们获得链表长度的时间复杂度就是O(1)了
    22     unsigned long len;
    23 
    24 } list;

    链表迭代器的结构如下:

     1 /*
     2  * 双端链表迭代器
     3  */
     4 typedef struct listIter {
     5 
     6     // 当前迭代到的节点
     7     listNode *next;
     8 
     9     // 迭代的方向
    10     int direction; //取值AL_START_HEAD等
    11 
    12 } listIter;

    里面涉及的函数中,增、删的比较简单,就是结构里面没有带头结点,所以需要单独判断一下头结点的特殊情况。另外对于尾节点的操作也需要考虑一下特殊情况。

    listAddNodeHead:将一个包含给定值的新节点添加到给定链表的表头

     1 /* Add a new node to the list, to head, contaning the specified 'value'
     2  * pointer as value.
     3  *
     4  * On error, NULL is returned and no operation is performed (i.e. the
     5  * list remains unaltered).
     6  * On success the 'list' pointer you pass to the function is returned. */
     7 /*
     8  * 将一个包含有给定值指针 value 的新节点添加到链表的表头
     9  *
    10  * 如果为新节点分配内存出错,那么不执行任何动作,仅返回 NULL
    11  *
    12  * 如果执行成功,返回传入的链表指针
    13  *
    14  * T = O(1)
    15  */
    16 list *listAddNodeHead(list *list, void *value)
    17 {
    18     listNode *node;
    19 
    20     // 为节点分配内存
    21     if ((node = zmalloc(sizeof(*node))) == NULL)
    22         return NULL;
    23 
    24     // 保存值指针
    25     node->value = value;
    26 
    27     // 添加节点到空链表
    28     if (list->len == 0) {
    29         list->head = list->tail = node;
    30         node->prev = node->next = NULL;
    31     // 添加节点到非空链表
    32     } else {
    33         node->prev = NULL;
    34         node->next = list->head;
    35         list->head->prev = node;
    36         list->head = node;
    37     }
    38 
    39     // 更新链表节点数
    40     list->len++;
    41 
    42     return list;
    43 }

    listAddNodeTail:将一个包含给定值的新节点添加到给定链表的表尾

     1 /* Add a new node to the list, to tail, containing the specified 'value'
     2  * pointer as value.
     3  *
     4  * On error, NULL is returned and no operation is performed (i.e. the
     5  * list remains unaltered).
     6  * On success the 'list' pointer you pass to the function is returned. */
     7 /*
     8  * 将一个包含有给定值指针 value 的新节点添加到链表的表尾
     9  *
    10  * 如果为新节点分配内存出错,那么不执行任何动作,仅返回 NULL
    11  *
    12  * 如果执行成功,返回传入的链表指针
    13  *
    14  * T = O(1)
    15  */
    16 list *listAddNodeTail(list *list, void *value)
    17 {
    18     listNode *node;
    19 
    20     // 为新节点分配内存
    21     if ((node = zmalloc(sizeof(*node))) == NULL)
    22         return NULL;
    23 
    24     // 保存值指针
    25     node->value = value;
    26 
    27     // 目标链表为空
    28     if (list->len == 0) {
    29         list->head = list->tail = node;
    30         node->prev = node->next = NULL;
    31     // 目标链表非空
    32     } else {
    33         node->prev = list->tail;
    34         node->next = NULL;
    35         list->tail->next = node;
    36         list->tail = node;
    37     }
    38 
    39     // 更新链表节点数
    40     list->len++;
    41 
    42     return list;
    43 }

    listInsertNode:将一个包含给定值的新节点添加到给定节点的之前或者之后

     1 /*
     2  * 创建一个包含值 value 的新节点,并将它插入到 old_node 的之前或之后
     3  *
     4  * 如果 after 为 0 ,将新节点插入到 old_node 之前。
     5  * 如果 after 为 1 ,将新节点插入到 old_node 之后。
     6  *
     7  * T = O(1)
     8  */
     9 list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
    10     listNode *node;
    11 
    12     // 创建新节点
    13     if ((node = zmalloc(sizeof(*node))) == NULL)
    14         return NULL;
    15 
    16     // 保存值
    17     node->value = value;
    18 
    19     // 将新节点添加到给定节点之后
    20     if (after) {
    21         node->prev = old_node;
    22         node->next = old_node->next;
    23         // 给定节点是原表尾节点
    24         if (list->tail == old_node) {
    25             list->tail = node;
    26         }
    27     // 将新节点添加到给定节点之前
    28     } else {
    29         node->next = old_node;
    30         node->prev = old_node->prev;
    31         // 给定节点是原表头节点
    32         if (list->head == old_node) {
    33             list->head = node;
    34         }
    35     }
    36 
    37     // 更新新节点的前置指针
    38     if (node->prev != NULL) {
    39         node->prev->next = node;
    40     }
    41     // 更新新节点的后置指针
    42     if (node->next != NULL) {
    43         node->next->prev = node;
    44     }
    45 
    46     // 更新链表节点数
    47     list->len++;
    48 
    49     return list;
    50 }

    listDelNode:从链表中删除给定节点

     1 /* Remove the specified node from the specified list.
     2  * It's up to the caller to free the private value of the node.
     3  *
     4  * This function can't fail. */
     5 /*
     6  * 从链表 list 中删除给定节点 node 
     7  * 
     8  * 对节点私有值(private value of the node)的释放工作由调用者进行。
     9  *
    10  * T = O(1)
    11  */
    12 void listDelNode(list *list, listNode *node)
    13 {
    14     // 调整前置节点的指针
    15     if (node->prev)
    16         node->prev->next = node->next;
    17     else
    18         list->head = node->next;
    19 
    20     // 调整后置节点的指针
    21     if (node->next)
    22         node->next->prev = node->prev;
    23     else
    24         list->tail = node->prev;
    25 
    26     // 释放值
    27     if (list->free) list->free(node->value);
    28 
    29     // 释放节点
    30     zfree(node);
    31 
    32     // 链表数减一
    33     list->len--;
    34 }

    listGetIterator:创建一个链表迭代器,并根据传入的direction来返回链表的头或尾节点

     1 /* Returns a list iterator 'iter'. After the initialization every
     2  * call to listNext() will return the next element of the list.
     3  *
     4  * This function can't fail. */
     5 /*
     6  * 为给定链表创建一个迭代器,
     7  * 之后每次对这个迭代器调用 listNext 都返回被迭代到的链表节点
     8  *
     9  * direction 参数决定了迭代器的迭代方向:
    10  *  AL_START_HEAD :从表头向表尾迭代
    11  *  AL_START_TAIL :从表尾想表头迭代
    12  *
    13  * T = O(1)
    14  */  //获取列表list的首部阶段或者尾部结点
    15 listIter *listGetIterator(list *list, int direction)
    16 {
    17     // 为迭代器分配内存
    18     listIter *iter;
    19     if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
    20 
    21     // 根据迭代方向,设置迭代器的起始节点
    22     if (direction == AL_START_HEAD)
    23         iter->next = list->head;
    24     else
    25         iter->next = list->tail;
    26 
    27     // 记录迭代方向
    28     iter->direction = direction;
    29 
    30     return iter;
    31 }

    listNext:拿到下一个迭代器节点

     1 /*
     2  * 返回迭代器当前所指向的节点。
     3  *
     4  * 删除当前节点是允许的,但不能修改链表里的其他节点。
     5  *
     6  * 函数要么返回一个节点,要么返回 NULL ,常见的用法是:
     7  *
     8  * iter = listGetIterator(list,<direction>);
     9  * while ((node = listNext(iter)) != NULL) {
    10  *     doSomethingWith(listNodeValue(node));
    11  * }
    12  *
    13  * T = O(1)
    14  */
    15 listNode *listNext(listIter *iter)
    16 {
    17     listNode *current = iter->next;
    18 
    19     if (current != NULL) {
    20         // 根据方向选择下一个节点
    21         if (iter->direction == AL_START_HEAD)
    22             // 保存下一个节点,防止当前节点被删除而造成指针丢失
    23             iter->next = current->next;
    24         else
    25             // 保存下一个节点,防止当前节点被删除而造成指针丢失
    26             iter->next = current->prev;
    27     }
    28 
    29     return current;
    30 }

    前面说了那么多操作,其实就是为了把它们组合起来使用,比如下面这个函数,就是上面的综合体。

    listDup:复制链表

     1 /* Duplicate the whole list. On out of memory NULL is returned.
     2  * On success a copy of the original list is returned.
     3  *
     4  * The 'Dup' method set with listSetDupMethod() function is used
     5  * to copy the node value. Otherwise the same pointer value of
     6  * the original node is used as value of the copied node.
     7  *
     8  * The original list both on success or error is never modified. */
     9 /*
    10  * 复制整个链表。
    11  *
    12  * 复制成功返回输入链表的副本,
    13  * 如果因为内存不足而造成复制失败,返回 NULL 。
    14  *
    15  * 如果链表有设置值复制函数 dup ,那么对值的复制将使用复制函数进行,
    16  * 否则,新节点将和旧节点共享同一个指针。
    17  *
    18  * 无论复制是成功还是失败,输入节点都不会修改。
    19  *
    20  * T = O(N)
    21  */
    22 list *listDup(list *orig)
    23 {
    24     list *copy;
    25     listIter *iter;
    26     listNode *node;
    27 
    28     // 创建新链表
    29     if ((copy = listCreate()) == NULL)
    30         return NULL;
    31 
    32     // 设置节点值处理函数
    33     copy->dup = orig->dup;
    34     copy->free = orig->free;
    35     copy->match = orig->match;
    36 
    37     // 迭代整个输入链表
    38     iter = listGetIterator(orig, AL_START_HEAD);
    39     while((node = listNext(iter)) != NULL) {
    40         void *value;
    41 
    42         // 复制节点值到新节点
    43         if (copy->dup) {
    44             value = copy->dup(node->value);
    45             if (value == NULL) {
    46                 listRelease(copy);
    47                 listReleaseIterator(iter);
    48                 return NULL;
    49             }
    50         } else
    51             value = node->value;
    52 
    53         // 将节点添加到链表
    54         if (listAddNodeTail(copy, value) == NULL) {
    55             listRelease(copy);
    56             listReleaseIterator(iter);
    57             return NULL;
    58         }
    59     }
    60 
    61     // 释放迭代器
    62     listReleaseIterator(iter);
    63 
    64     // 返回副本
    65     return copy;
    66 }

    最后再提一个旋转函数listRotate,也没有什么需要过多解释的。

     1 /* Rotate the list removing the tail node and inserting it to the head. */
     2 /*
     3  * 取出链表的表尾节点,并将它移动到表头,成为新的表头节点。
     4  *
     5  * T = O(1)
     6  */
     7 void listRotate(list *list) {
     8     listNode *tail = list->tail;
     9 
    10     if (listLength(list) <= 1) return;
    11 
    12     /* Detach current tail */
    13     // 取出表尾节点
    14     list->tail = tail->prev;
    15     list->tail->next = NULL;
    16 
    17     /* Move it as head */
    18     // 插入到表头
    19     list->head->prev = tail;
    20     tail->prev = NULL;
    21     tail->next = list->head;
    22     list->head = tail;
    23 }

    总体来说,链表的数据结构是属于比较简单的。

  • 相关阅读:
    Java开发环境搭建——CentOS配置
    Sqlite使用
    Java加载资源文件几种方法
    JavaScript工具代码
    Mysql分区的技能
    HBase数据迁移至Hive
    Shell或notepad连接虚拟机操作
    json的工具按照键进行排序
    Eclipse中SVN修改的*星号没了,解决方法
    同时安装不同版本JDK遇到的问题
  • 原文地址:https://www.cnblogs.com/abc-begin/p/7532985.html
Copyright © 2011-2022 走看看