zoukankan      html  css  js  c++  java
  • 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
    ");//查看当前循环次数
            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!
    ");
        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
    ",res->data);
        }
    
    //    pop  push测试
        RDLinkList  linkList2 = initialLink();
        lpush(linkList2,100);
        lpush(linkList2,200);
        rpush(linkList2,300);
        rpush(linkList2,400);
        printf("%d
    ",lpop(linkList2)->data);
        printf("%d
    ",lpop(linkList2)->data);
        printf("%d
    ",rpop(linkList2)->data);
        printf("%d
    ",rpop(linkList2)->data);
        return 0;
    }
    

    从同一端推入和弹出 如:lpush和lpop 能实现栈

    从相反方向推入和弹出 如:lpush和rpop能实现队列

  • 相关阅读:
    spring 事务管理
    snmp4j 异步获取节点信息
    snmp4j 过滤错误的节点
    国际化支持
    通过Java反射机制获取对象的方法
    jdk中有哪些包不用自动导入
    位运算符
    spring 从入门到精通 (一)
    命令行工具nslookup查域名DNS服务器
    java/php DES/CBC/PKCS5Padding加密解密算法实现过程
  • 原文地址:https://www.cnblogs.com/yangyuanhu/p/12390199.html
Copyright © 2011-2022 走看看