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

相关文章
相关标签/搜索