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

雙向鏈表

雙向表示每一個節點知道本身的直接前驅和直接後繼,每一個節點須要三個域redis

查找方向能夠是從左往右也能夠是從右往左,可是要實現從右往左還須要終端節點的地址,因此一般會設計成雙向的循環鏈表;oop

雙向的循環鏈表

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

若鏈表爲空 則head的next和prior都是head本身設計

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

也能夠實現redis中list類型能夠從兩端插入或取值blog

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 能實現棧get

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

相關文章
相關標籤/搜索