Redis源碼閱讀—數(shù)據(jù)結(jié)構(gòu)之雙端鏈表 adlist.h/adlist.c

adlist.h/adlist.c


一、 adlist 的定義

由于 C 語言沒有內(nèi)置的鏈表這種常用的數(shù)據(jù)結(jié)構(gòu),因此 Redis 實現(xiàn)了自己的鏈表實現(xiàn)。

Redis 對鏈表的實現(xiàn),包括鏈表( list )、鏈表節(jié)點( listNode )以及鏈表迭代器( listIter )三個數(shù)據(jù)結(jié)構(gòu)。

其中,鏈表節(jié)點在 adlist.h/listNode 中進行了定義:


//鏈表節(jié)點
typedef struct listNode {

    // 前置節(jié)點
    struct listNode *prev;
    
    // 后置節(jié)點
    struct listNode *next;
    
    // 節(jié)點的值
    void *value;
    
} listNode;

多個鏈表節(jié)點通過 prve 和 next 指針組成雙端鏈表,如下圖所示:

由多個鏈表節(jié)點組成的雙端鏈表

在鏈表節(jié)點組成雙端鏈表后,由鏈表( adlist.h/list )來持有雙端鏈表的話,操作起來會更加方便。

鏈表在 adlist.h/list 中進行了定義:


//鏈表
typedef struct list {

    // 表頭節(jié)點
    listNode *head;
    
    // 表尾節(jié)點
    listNode *tail;
    
    // 鏈表長度,即鏈表所包含的節(jié)點數(shù)量
    unsigned long len;
    
    // 節(jié)點值復(fù)制函數(shù)
    void *(*dup)(void *ptr);
    
    // 節(jié)點值釋放函數(shù)
    void (*free)(void *ptr);
    
    // 節(jié)點值對比函數(shù)
    int (*match)(void *ptr, void *key);
    
} list;

下圖是一個 list 結(jié)構(gòu)與三個 listNode 結(jié)構(gòu)組成的鏈表:

由 list 結(jié)構(gòu)與 listNode 結(jié)構(gòu)組成的鏈表

迭代器在 adlist.h/listIter 中進行了定義:


//迭代器
typedef struct listIter {

    // 當前迭代到的節(jié)點
    listNode *next;
    
    // 迭代的方向
    // 0 表示從表頭向表尾進行迭代
    // 1 表示從表尾向表頭進行迭代
    int direction;  
    
} listIter;

//迭代器進行迭代的方向
 
// 從表頭向表尾進行迭代
#define AL_START_HEAD 0

// 從表尾到表頭進行迭代
#define AL_START_TAIL 1


二、 adlist 的 API

adlist 的函數(shù)實現(xiàn)分為兩種,一種是通過宏定義的函數(shù),一種是普通函數(shù)。

  1. 宏定義函數(shù)
函數(shù) 作用 時間復(fù)雜度
listLength 返回給定鏈表的長度,即所包含的節(jié)點個數(shù) O(1)
listFirst 返回給定鏈表的表頭節(jié)點 O(1)
listLast 返回給定鏈表的表尾節(jié)點 O(1)
listPrevNode 返回給定鏈表節(jié)點的前一個結(jié)點 O(1)
listNextNode 返回給定鏈表節(jié)點的后一個結(jié)點 O(1)
listNodeValue 返回給定鏈表節(jié)點的值 O(1)
listSetDupMethod 將給定的函數(shù)設(shè)置為給定鏈表的節(jié)點值復(fù)制函數(shù) O(1)
listSetFreeMethod 將給定的函數(shù)設(shè)置為給定鏈表的節(jié)點值釋放函數(shù) O(1)
listSetMatchMethod 將給定的函數(shù)設(shè)置為給定鏈表的節(jié)點值對比函數(shù) O(1)
listGetDupMethod 返回給定鏈表當前正在使用的節(jié)點值復(fù)制函數(shù) O(1)
listGetFree 返回給定鏈表當前正在使用的節(jié)點值釋放函數(shù) O(1)
listGetMatchMethod 返回給定鏈表當前正在使用的節(jié)點值對比函數(shù) O(1)
  1. 普通函數(shù)
函數(shù) 作用 時間復(fù)雜度
listCreate 創(chuàng)建一個不包含任何節(jié)點的空鏈表 O(1)
listRelease 釋放給定鏈表,包括鏈表中的所有節(jié)點 O(N),N為鏈表中的節(jié)點數(shù)
listAddNodeHead 將包含給定值的新節(jié)點插入到給定鏈表的表頭 O(1)
listAddNodeTail 將包含給定值的新節(jié)點插入到給定鏈表的表尾 O(1)
listInsertNode 將包含給定值的新節(jié)點插入到給定節(jié)點的之前或之后 O(1)
listDelNode 從給定鏈表中刪除給定節(jié)點 O(1)
listGetIterator 為給定鏈表創(chuàng)建一個迭代器,并為其配置迭代方向 O(1)
listNext 返回迭代器當前所指向的鏈表節(jié)點 O(1)
listReleaseIterator 釋放給定迭代器 O(1)
listDup 復(fù)制給定鏈表,并返回復(fù)制后的副本 O(N),N為鏈表中的節(jié)點數(shù)
listSearchKey 查找并返回鏈表中包含給定值的結(jié)點,查找方向為從頭至尾 O(N),N為鏈表中的節(jié)點數(shù)
listIndex 查找并返回鏈表中在給定索引上的結(jié)點,索引值從零開始,可以為負(索引為負表示從表尾開始查找,-1 表示表尾節(jié)點,-2 表示倒數(shù)第二個結(jié)點) O(N),N為鏈表中的節(jié)點數(shù)
listRewind 將給定迭代器的方向設(shè)置為從頭到尾(AL_START_HEAD),并將迭代指針重新指向表頭節(jié)點 O(1)
listRewindTail 將給定迭代器的方向設(shè)置從尾到頭(AL_START_TAIL),并將迭代指針重新指向表尾節(jié)點 O(1)
listRotate 將鏈表的表尾結(jié)點彈出,并插入到鏈表的表頭,成為新的表頭結(jié)點 O(1)

三、 adlist 的特性

  1. 雙端鏈表
    鏈表節(jié)點有 prevnext 指針,所以獲取某節(jié)點的前后結(jié)點的復(fù)雜度都為O(1)。

  2. 無環(huán)
    鏈表無環(huán),即鏈表的表頭結(jié)點的 prev 指針和表尾節(jié)點的 next 指針都指向 NULL ,所以在通過迭代器對鏈表進行訪問時(無論方向),都會以 NULL 為結(jié)尾,不會出現(xiàn)循環(huán)。

  3. 帶有表頭結(jié)點和表尾結(jié)點
    list 結(jié)構(gòu)帶有 headtail 指針,所以獲取某鏈表的頭尾結(jié)點的復(fù)雜度都為O(1)。

  4. 帶有鏈表長度
    list 結(jié)構(gòu)帶有 len 屬性,所以獲取某鏈表的長度的復(fù)雜度僅為O(1)。

  5. 多態(tài)
    鏈表節(jié)點使用 void * 指針來持有節(jié)點值,所以鏈表可以用于保存各種類型的值。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容