一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|數(shù)據(jù)庫技術|

服務器之家 - 數(shù)據(jù)庫 - Redis - 詳解Redis中的雙鏈表結(jié)構(gòu)

詳解Redis中的雙鏈表結(jié)構(gòu)

2019-10-26 19:55zinss26914 Redis

這篇文章主要介紹了Redis中的雙鏈表結(jié)構(gòu),包括listNode結(jié)構(gòu)的API,需要的朋友可以參考下

Redis中雙鏈表實現(xiàn)的基本結(jié)構(gòu):
1.節(jié)點結(jié)構(gòu)

?
1
2
3
4
5
typedef struct listNode {
  struct listNode *prev; //前向節(jié)點
  struct listNode *next; //后向節(jié)點
  void *value;       //該節(jié)點的值
} listNode;

2.雙向鏈表結(jié)構(gòu)

?
1
2
3
4
5
6
7
8
typedef struct list {
  listNode *head;       //頭節(jié)點
  listNode *tail;        //尾節(jié)點
  void *(*dup)(void *ptr); //復制函數(shù)
  void (*free)(void *ptr);  //釋放函數(shù)
  int (*match)(void *ptr, void *key); //匹配函數(shù),查找節(jié)點使用
  unsigned long len;     //雙向鏈表的長度即節(jié)點的個數(shù)
} list;

3.雙向鏈表遍歷器

?
1
2
3
4
5
6
7
8
9
typedef struct listIter {
  listNode *next;  //下一個節(jié)點
  int direction;
} listIter;
 
 方向定義
 
  #define AL_START_HEAD 0 //向前查找
  #define AL_START_TAIL 1  //向后查找

4.宏定義函數(shù)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define listLength(l) ((l)->len)
#define listFirst(l) ((l)->head)
#define listLast(l) ((l)->tail)
#define listPrevNode(n) ((n)->prev)
#define listNextNode(n) ((n)->next)
#define listNodeValue(n) ((n)->value)
 
#define listSetDupMethod(l,m) ((l)->dup = (m))
#define listSetFreeMethod(l,m) ((l)->free = (m))
#define listSetMatchMethod(l,m) ((l)->match = (m))
 
#define listGetDupMethod(l) ((l)->dup)
#define listGetFree(l) ((l)->free)
#define listGetMatchMethod(l) ((l)->match)

5.定義函數(shù)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
list *listCreate(void); //創(chuàng)建一個新的鏈表。該鏈表可以使用AlFree()方法釋放。
 
               //但使用AlFree()方法前需要釋放用戶釋放私有節(jié)點的值。
 
               //如果沒有創(chuàng)建成功,返回null;創(chuàng)建成功則返回指向新鏈表的指針。
 
 
void listRelease(list *list); //釋放整個鏈表,此函數(shù)不會執(zhí)行失敗。調(diào)用zfree(list *list)方法,定義在Zmalloc.c中。
 
 
list *listAddNodeHead(list *list, void *value); //向鏈表頭部中增加一個節(jié)點
 
 
list *listAddNodeTail(list *list, void *value);  //向鏈表尾部增加一個節(jié)點
 
 
list *listInsertNode(list *list, listNode *old_node, void *value, int after);//向某個節(jié)點位置插入節(jié)點 after為方向
 
 
void listDelNode(list *list, listNode *node);//從鏈表上刪除特定節(jié)點,調(diào)用者釋放特定私用節(jié)點的值。
 
                              //該函數(shù)不會執(zhí)行失敗
listIter *listGetIterator(list *list, int direction);//返回某個鏈表的迭代器。
 
                                 //迭代器的listNext()方法會返回鏈表的下個節(jié)點。direction是方向
 
                                //該函數(shù)不會執(zhí)行失敗。
 
 
listNode *listNext(listIter *iter);       
 
 
void listReleaseIterator(listIter *iter);      //釋放迭代器的內(nèi)存。
 
 
list *listDup(list *orig);                //復制整個鏈表。當內(nèi)存溢出時返回null,成功時返回原鏈表的一個備份
 
                                //不管該方法是否執(zhí)行成功,原鏈表不會改變。
 
 
listNode *listSearchKey(list *list, void *key); //從特定的鏈表查找key。成功則返回第一個匹配節(jié)點的指針
 
                                //如果沒有匹配,則返回null。
 
 
listNode *listIndex(list *list, long index);   //序號從0開始,鏈表的頭的索引為0.1為頭節(jié)點的下個節(jié)點。一次類推。
 
                            //負整數(shù)用來表示從尾部開始計數(shù)。-1表示最后一個節(jié)點,-2倒數(shù)第二個節(jié)點
 
                             //如果超過鏈表的索引,則返回null
 
 
void listRewind(list *list, listIter *li) {
  li->next = list->head;
  li->direction = AL_START_HEAD;
}
 
void listRewindTail(list *list, listIter *li) {
  li->next = list->tail;
  li->direction = AL_START_TAIL;
}
 
 
void listRotate(list *list);         //旋轉(zhuǎn)鏈表,移除尾節(jié)點并插入頭部。

 

list結(jié)構(gòu)和listNode結(jié)構(gòu)的API
list和listNode都有它們自己的一族API,這里貼出來學習一下redis的源碼(ps:下面的代碼都是我仿照redis改寫能直接編譯運行的代碼)

list *listCreate(void)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * 創(chuàng)建一個新列表
 *
 * T = O(1)                                                              
 */
list *listCreate(void)
{
  struct list *list;
 
  // 為列表結(jié)構(gòu)分配內(nèi)存
  list = (struct list *)malloc(sizeof(struct list));
  if (list == NULL)
    return NULL;
 
  // 初始化屬性
  list->head = list->tail = NULL;
  list->len = 0;
  list->dup = NULL;
  list->free = NULL;
  list->match = NULL;
 
  return list;
}


void listRelease(list *list)

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * 釋放整個列表
 *
 * T = O(N), N為列表長度
 */
void listRelease(list *list)
{
  unsigned long len;
  listNode *current, *next;
 
  current = list->head;
  len = list->len;
 
  while (len --) {
    next = current->next;
    // 如果列表有自帶的free方法,那么先對節(jié)點值調(diào)用它
    if (list->free) list->free(current->value);
    // 之后釋放節(jié)點
    free(current);
    current = next;
  }
  free(list);
}

list *listAddNodeHead(list *list, void *value)
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
 * 新建一個包含給定value的節(jié)點,并將它加入到列表的表頭
 *
 * T = O(1)                                                              
 */
list *listAddNodeHead(list *list, void *value)
{
  listNode *node;
 
  node = (listNode *)malloc(sizeof(listNode));
  if (node == NULL)
    return NULL;
 
  node->value = value;
 
  if (list->len == 0) {
    // 第一個節(jié)點
    list->head = list->tail = node;
    node->prev = node->next = NULL;
  } else {
    // 不是第一個節(jié)點
    node->prev = NULL;
    node->next = list->head;
    list->head->prev = node;
    list->head = node;
  }
 
  list->len ++;
 
  return list;
}


list *listAddNodeTail(list *list, void *value)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
 * 新建一個包含給定value的節(jié)點,并把它加入到列表的表尾
 *
 * T = O(1)
 */
list *listAddNodeTail(list *list, void *value)
{
  listNode *node;
   
  node = (listNode *)malloc(sizeof(listNode));
  if (node == NULL)
    return NULL;
 
  if (list->len == 0) {
    // 第一個節(jié)點
    list->head = list->tail = node;
    node->prev = node->next = NULL;
  } else {
    // 不是第一節(jié)點
    node->prev = list->tail;
    node->next = NULL;
    list->tail->next = node;
    list->tail = node;
  }
 
  list->len ++;
 
  return list;
}


list *listInsertNode(list *list, listNode *old_node, void *value, int after)

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
 * 創(chuàng)建一個包含值value的節(jié)點
 * 并根據(jù)after參數(shù)的指示,將新節(jié)點插入到old_node的之前或者之后
 *
 * T = O(1)
 */
list *listInsertNode(list *list, listNode *old_node, void *value, int after)
{
  listNode *node;
 
  node = (listNode *)malloc(sizeof(listNode));
  if (node == NULL)
    return NULL;
 
  if (after) {
    // 插入到old_node之后
    node->prev = old_node;
    node->next = old_node->next;
    // 處理表尾節(jié)點
    if (list->tail == old_node) {
      list->tail = node;
    }
  } else {
    // 插入到old_node之前
    node->next = old_node;
    node->prev = old_node->prev;
    // 處理表頭節(jié)點
    if (list->head == old_node) {
      list->head = node;
    }
  }
 
  // 更新前置節(jié)點和后繼節(jié)點的指針(這個地方很經(jīng)典,節(jié)約代碼)
  if (node->prev != NULL) {
    node->prev->next = node;
  }
  if (node->next != NULL) {
    node->next->prev = node;
  }
 
  // 更新列表節(jié)點
  list->len ++;
 
  return list;
}


void listDelNode(list *list, listNode *node)

  

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
  * 釋放列表中給定的節(jié)點
  *
  * T = O(1)
  */
 void listDelNode(list *list, listNode *node)
 {
   // 處理前驅(qū)節(jié)點指針
   if (node->prev) {
     node->prev->next = node->next;
   } else {
     list->head = node->next;
   }
  
   // 處理后繼節(jié)點
   if (node->next) {
     node->next->prev = node->prev;
   } else {
     list->tail = node->prev;
   }
  
   // 釋放節(jié)點值
   if (list->free) list->free(node->value);
  
   // 釋放節(jié)點
   free(node);
  
   // 更新列表節(jié)點數(shù)目
   list->len --;
 }


迭代器
其實我對迭代器的概念非常陌生,因為我是純c程序員,不會c++,這里直接跟著學了!

Redis針對list結(jié)構(gòu)實現(xiàn)了一個迭代器,用于對鏈表進行遍歷

迭代器的結(jié)構(gòu)定義如下:

?
1
2
3
4
5
6
7
8
9
10
/**
 * 鏈表迭代器
 */
typedef struct listIter {
  // 下一節(jié)點
  listNode *next;
 
  // 迭代方向
  int direction;
} listIter;


direction決定了迭代器是沿著next指針向后迭代,還是沿著prev指針向前迭代,這個值可以是adlist.h中的AL_START_HEAD常量或AL_START_TAIL常量:

?
1
2
#define AL_START_HEAD 0
#define AL_START_TAIL 1


學習一下迭代器的api實現(xiàn):

listIter *listGetIterator(list *list, int direction)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * 創(chuàng)建列表list的一個迭代器,迭代方向由參數(shù)direction決定
 *
 * 每次對迭代器listNext(),迭代器返回列表的下一個節(jié)點
 *
 * T = O(1)
 */
listIter *listGetIterator(list *list, int direction)
{
  listIter *iter;
 
  iter = (listIter *)malloc(sizeof(listIter));
  if (iter == NULL)
    return NULL;
 
  // 根據(jù)迭代器的方向,將迭代器的指針指向表頭或者表尾
  if (direction == AL_START_HEAD) {
    iter->next = list->head;
  } else {
    iter->next = list->tail;
  }
 
  // 記錄方向
  iter->direction = direction;
 
  return iter;
}


void listRewind(list *list, listIter *li)

?
1
2
3
4
5
6
7
8
9
10
/**
 * 將迭代器iter的迭代指針倒回list的表頭
 *
 * T = O(1)
 */
void listRewind(list *list, listIter *li)
{
  li->next = list->head;
  li->direction = AL_START_HEAD;
}


void listRewindTail(list *list, listIter *li)

?
1
2
3
4
5
6
7
8
9
10
/**
 * 將迭代器iter的迭代指針倒回list的表尾
 *
 * T = O(1)
 */
void listRewindTail(list *list, listIter *li)
{
  li->next = list->tail;
  li->direction = AL_START_TAIL;
}


listNode *listNext(listIter *iter)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * 函數(shù)要么返回當前節(jié)點,要么返回NULL,因此,常見的用法是:
 * iter = listGetIterator(list, <direction>);
 * while ((node = listNext(iter)) != NULL) {
 *   doSomethingWith(listNodeValue(node));
 * }
 *
 * T = O(1)
 */
listNode *listNext(listIter *iter)
{
  listNode *current = iter->next;
 
  if (current != NULL) {
    // 根據(jù)迭代方向,選擇節(jié)點
    if (iter->direction == AL_START_HEAD)
      iter->next = current->next;
    else
      iter->next = current->prev;
  }
 
  return current;
}

延伸 · 閱讀

精彩推薦
  • Redisredis 交集、并集、差集的具體使用

    redis 交集、并集、差集的具體使用

    這篇文章主要介紹了redis 交集、并集、差集的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友...

    xiaojin21cen10152021-07-27
  • RedisRedis全量復制與部分復制示例詳解

    Redis全量復制與部分復制示例詳解

    這篇文章主要給大家介紹了關于Redis全量復制與部分復制的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Redis爬蟲具有一定的參考學習...

    豆子先生5052019-11-27
  • RedisRedis如何實現(xiàn)數(shù)據(jù)庫讀寫分離詳解

    Redis如何實現(xiàn)數(shù)據(jù)庫讀寫分離詳解

    Redis的主從架構(gòu),能幫助我們實現(xiàn)讀多,寫少的情況,下面這篇文章主要給大家介紹了關于Redis如何實現(xiàn)數(shù)據(jù)庫讀寫分離的相關資料,文中通過示例代碼介紹...

    羅兵漂流記6092019-11-11
  • RedisRedis的配置、啟動、操作和關閉方法

    Redis的配置、啟動、操作和關閉方法

    今天小編就為大家分享一篇Redis的配置、啟動、操作和關閉方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    大道化簡5312019-11-14
  • Redisredis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    redis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    這篇文章主要給大家介紹了關于redis中如何使用lua腳本讓你的靈活性提高5個逼格的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具...

    一線碼農(nóng)5812019-11-18
  • Redisredis實現(xiàn)排行榜功能

    redis實現(xiàn)排行榜功能

    排行榜在很多地方都能使用到,redis的zset可以很方便地用來實現(xiàn)排行榜功能,本文就來簡單的介紹一下如何使用,具有一定的參考價值,感興趣的小伙伴們...

    乘月歸5022021-08-05
  • Redis詳解Redis復制原理

    詳解Redis復制原理

    與大多數(shù)db一樣,Redis也提供了復制機制,以滿足故障恢復和負載均衡等需求。復制也是Redis高可用的基礎,哨兵和集群都是建立在復制基礎上實現(xiàn)高可用的...

    李留廣10222021-08-09
  • RedisRedis 事務知識點相關總結(jié)

    Redis 事務知識點相關總結(jié)

    這篇文章主要介紹了Redis 事務相關總結(jié),幫助大家更好的理解和學習使用Redis,感興趣的朋友可以了解下...

    AsiaYe8232021-07-28
主站蜘蛛池模板: 99精品网站 | 四虎网站网址 | 久久久精品国产免费A片胖妇女 | 视频在线观看入口一二三2021 | 天天视频国产精品 | 香蕉国产人午夜视频在线观看 | 国产精品久久久久久久午夜片 | 桃子视频www | 免费视频精品一区二区三区 | 小便japanesewctv | yjzz视频 | 从后面撕开老师的丝袜动态图 | 性一交一乱一伧老太 | 国产主播99| 高清视频在线播放 | 欧美成人tv | 黑帮少爷爱上我第8集在线观看 | 我与么公激情性完整视频 | 美女翘臀跪床被打屁股作文 | 天堂俺去俺来也www久久婷婷 | 亚洲 制服 欧美 中文字幕 | 午夜香蕉成视频人网站高清版 | 女人用粗大自熨喷水在线视频 | 国产区久久 | 999精品视频这里只有精品 | 国产微拍精品一区 | 免费一级特黄特色大片在线 | 女人被爽到呻吟娇喘的视频动态图 | 精品久久久久久午夜 | 亚洲欧美国产在线 | 欧美生活一级片 | 欧美视频一级 | 国产伦精品一区二区三区免费迷 | 欧洲老太玩小伙 | 精品国产欧美一区二区三区成人 | 欧美肥b | 美国女艳星brandilove | 日韩欧美成末人一区二区三区 | 男女被爆动漫羞羞动漫 | 日韩二区三区 | 国产成人精品第一区二区 |