Redis list實現原理 - 雙向循環鏈表


雙向鏈表

雙向表示每個節點知道自己的直接前驅和直接后繼,每個節點需要三個域

查找方向可以是從左往右也可以是從右往左,但是要實現從右往左還需要終端節點的地址,所以通常會設計成雙向的循環鏈表;

雙向的循環鏈表

循環鏈表指得是終端節點的next指向head節點,head的prior指向終端節點

若鏈表為空 則head的next和prior都是head自己

與普通鏈表不同之處就在於可以根據要查找的位置來決定遍歷方向從而降低遍歷次數,當要查找的數據在兩端時效率更優

也可以實現redis中list類型可以從兩端插入或取值

c語言實現:

#include <stdio.h>
#include <stdlib.h>
//定義節點結構
typedef struct Node {
    struct Node *next, *prior;
    int data, length;
} Node, *RDLinkList;

//初始化鏈表
RDLinkList initialLink() {
    Node *head = malloc(sizeof(Node));
    head->next = head; //next和prior都指向自身
    head->prior = head;
    head->length = 0;
    return head;
}
//獲取指定位置的節點
Node *get(RDLinkList list, int position) {
    if (position<1 || position > list->length){
        return NULL;
    }
    Node *current;
    int index,reverse;
    //判斷要獲取的位置在左邊還是右邊,從而確定遍歷方向,以減少遍歷次數
    if (position <= (list->length / 2)){
        //目標位置小於等於中心位置 從左邊開始
        index = 1;
        current = list->next;//指向首節點
        reverse = 0;
    }else{
        //目標位置大於中心位置 從右邊開始
        index = list->length;
        current = list->prior;//指向終端節點
        reverse = 1;
    }
    //如果下面還有值並且還沒有到達指定的位置就繼續遍歷
    while (current != list && position != index){
        printf("loop\n");//查看當前循環次數
        if (reverse == 1){
            current = current->prior;
            index -= 1;
        }else{
            current = current->next;
            index += 1;
        }
    }
    if (index == position && current!=list) {
        return current;
    }
    return NULL;
}
//插入一個新節點到指定位置
void insert(RDLinkList list, int data, int position) {
    Node *newNode, *pre;
    if (position == 1) {
        pre = list;
    } else {
        pre = get(list, position - 1);
    }
    //判斷其位置是否可插入
    if (pre == NULL) {
        printf("位置非法");
        exit(-1);
    }
    newNode = malloc(sizeof(Node));
    newNode->data = data;

    newNode->next = pre->next;
    newNode->prior = pre;
    pre->next = newNode;
    newNode->next->prior = newNode;
    list->length += 1;
}
//刪除某個位置節點
void delete(RDLinkList list,int position){
    Node * target = get(list,position);
    if (target != NULL){
        target->prior->next = target->next;
        target->next->prior = target->prior;
        free(target);
    }
    list->length-=1;
}

//插入到左邊
void lpush(RDLinkList list,int data){
    insert(list,data,1);
}
//插入到右邊
void rpush(RDLinkList list,int data){
    insert(list,data,list->length+1);
}
Node * pop(RDLinkList list,int left){
    Node *target;
    if (left == 1){
         target = get(list,1);
    } else{
        target = get(list,list->length);
    }
    if (target != NULL){
        target->prior->next = target->next;
        target->next->prior = target->prior;
        free(target);
    }
    return target;
}
//彈出最左邊一個元素
Node *lpop(RDLinkList list){
    return pop(list,1);
}
//彈出最右邊一個元素
Node *rpop(RDLinkList list){
    return pop(list,0);
}

int main() {
    printf("Hello, World!\n");
    RDLinkList  linkList = initialLink();
    insert(linkList,100,1);
    insert(linkList,200,2);
    insert(linkList,300,3);
    insert(linkList,400,4);
    insert(linkList,500,5);
    insert(linkList,600,6);
    insert(linkList,700,7);
    insert(linkList,800,8);
    insert(linkList,900,9);
    insert(linkList,1000,10);
    insert(linkList,1100,11);
    //查找測試 從右邊遍歷 只需要遍歷兩個節點就能找到第9個
    Node *res = get(linkList,9);
    if (res != NULL){
        printf("%d\n",res->data);
    }

//    pop  push測試
    RDLinkList  linkList2 = initialLink();
    lpush(linkList2,100);
    lpush(linkList2,200);
    rpush(linkList2,300);
    rpush(linkList2,400);
    printf("%d\n",lpop(linkList2)->data);
    printf("%d\n",lpop(linkList2)->data);
    printf("%d\n",rpop(linkList2)->data);
    printf("%d\n",rpop(linkList2)->data);
    return 0;
}

從同一端推入和彈出 如:lpush和lpop 能實現棧

從相反方向推入和彈出 如:lpush和rpop能實現隊列


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM