从[redis:LinkedList]中学习链表

news2025/4/25 17:52:15

文章目录

  • adlist
    • listNode
    • list
    • macros[宏定义]
    • listCreate
    • listInitNode
    • listEmpty
    • listRelease
    • listAddNodeHead
    • listLinkNodeHead
    • listAddNodeTail
    • listLinkNodeTail
    • listInsertNode
    • listDelNode
    • listUlinkNode
    • listIndex
    • redis3.2.100quicklist
    • redis7.2.2quicklist

redis的基本数据类型之一"list"的底层编码从"ziplist"和"LinkedList"[3.2版本之前]升级到"由ziplist组成的LinkedList"[3.2版本及以后]再升级到"由listpack组成的LinkedList"[7.2.2版本]。

本篇文章介绍"LinkedList"

adlist

adlist.h - A generic doubly linked list implementation

adlist 是一个通用的双向链表

listNode

首先看一下链表节点是如何定义的

typedef struct listNode {
    struct listNode *prev;//指向前一个节点的指针
    struct listNode *next;//指向后一个节点的指针
    void *value;
} listNode;

list

链表的定义

typedef struct list {
    listNode *head;//头节点
    listNode *tail;//尾节点
    /*
    以下是一些指向"实现特定功能的函数"的指针
    */
    void *(*dup)(void *ptr);//dup指向传入参数类型和返回值类型都是"void*"的函数
    void (*free)(void *ptr);//dup指向传入参数类型和返回值类型都是"void*"的函数
    int (*match)(void *ptr, void *key);//dup指向传入参数类型是两个"void *"和返回值类型都是"int*"的函数
    unsigned long len;//记录list中的元素个数
} list;
  • 获取list的元素个数的时间复杂度为O(1)因为链表的定义中包含记录节点个数的字段len

macros[宏定义]

来看看都定义了哪些宏方法。

//获取长度,由此可以看出对于OBJ_ENCODING_LINKEDLIST 编码方式的list来说,获取长度的时间复杂度为O(1)
#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)
//获取节点指向的value值
#define listNodeValue(n) ((n)->value)

listCreate

//创建一个list
list *listCreate(void)
{
    struct list *list;
	//分配内存失败
    if ((list = zmalloc(sizeof(*list))) == NULL)
        return NULL;
    //分配内存成功
    //初始化
    list->head = list->tail = NULL;
    list->len = 0;
    list->dup = NULL;
    list->free = NULL;
    list->match = NULL;
    return list;
}

listInitNode

//初始化链表节点,将前后指针置为空,value值置为对应的value值
void listInitNode(listNode *node, void *value) {
    node->prev = NULL;
    node->next = NULL;
    node->value = value;
}

listEmpty

//清空list,只是将节点从链表中移除而不销毁链表
/* Remove all the elements from the list without destroying the list itself. */
void listEmpty(list *list)
{
    unsigned long len;
    listNode *current, *next;

    current = list->head;
    len = list->len;
    //逐一释放每个节点
    while(len--) {
    	//先保存下一个节点
        next = current->next;
        //释放当前节点value指向的内存
        if (list->free) list->free(current->value);
        //释放当前节点
        zfree(current);
        current = next;
    }
    //头尾节点设置为NULL,len设置为0
    list->head = list->tail = NULL;
    list->len = 0;
}

listRelease

/* Free the whole list.
 *
 * This function can't fail. */
void listRelease(list *list)
{
    listEmpty(list);
    //释放list本身
    zfree(list);
}

listAddNodeHead

list *listAddNodeHead(list *list, void *value)
{
    listNode *node;
    //为新结点分配内存
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;
    node->value = value;
    //从头部插入list
    listLinkNodeHead(list, node);
    return list;
}

listLinkNodeHead

void listLinkNodeHead(list* list, listNode *node) {
    if (list->len == 0) {
    //第一次插入节点
        list->head = list->tail = node;
        node->prev = node->next = NULL;
    } else {
        node->prev = NULL;
        node->next = list->head;
        list->head->prev = node;
        list->head = node;
    }
    //元素个数加1
    list->len++;
}

在这里插入图片描述

listAddNodeTail

list *listAddNodeTail(list *list, void *value)
{
    listNode *node;
	//为新节点分配内存
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;
    node->value = value;
    listLinkNodeTail(list, node);
    return list;
}

listLinkNodeTail

void listLinkNodeTail(list *list, listNode *node) {
    if (list->len == 0) {
    //第一次插入节点
        list->head = list->tail = node;
        node->prev = node->next = NULL;
    } else {
        node->prev = list->tail;
        node->next = NULL;
        list->tail->next = node;
        list->tail = node;
    }
    //元素个数加1
    list->len++;
}

在这里插入图片描述

listInsertNode

//如果after为1则在old_node之后插入新节点
//反之在old_node之前插入新节点

list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
    listNode *node;
    //为node分配内存
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;
    node->value = value;
    //在old_node之后插入新节点
    if (after) {
        node->prev = old_node;
        node->next = old_node->next;
        //如果old_node是尾节点,插入之后要修改tail
        if (list->tail == old_node) {
            list->tail = node;
        }
    } else {
    //在old_node之前插入新节点
        node->next = old_node;
        node->prev = old_node->prev;
        //如果old_node是头节点,插入之后要修改head
        if (list->head == old_node) {
            list->head = node;
        }
    }
    if (node->prev != NULL) {
        node->prev->next = node;
    }
    if (node->next != NULL) {
        node->next->prev = node;
    }
    list->len++;
    return list;
}

listDelNode

void listDelNode(list *list, listNode *node)
{
    listUnlinkNode(list, node);
    //移除node并释放内存
    if (list->free) list->free(node->value);
    zfree(node);
}

listUlinkNode

//从list中移除node
void listUnlinkNode(list *list, listNode *node) {
	//node->prev不为空说明node不是第一个元素
    if (node->prev)
        node->prev->next = node->next;
    else
    	//node是第一个元素,删除之后需要修改head节点
        list->head = node->next;
    //node不是最后一个元素
    if (node->next)
        node->next->prev = node->prev;
    else
    //node是最后一个元素,删除之后需要修改tail节点
        list->tail = node->prev;
	
	//从list中移除node
    node->next = NULL;
    node->prev = NULL;
	//元素个数减1
    list->len--;
}

listIndex

/*
寻找list中第index个元素,正序遍历index从0开始,0代表head
倒序遍历从-1开始,-1指向tail
*/
listNode *listIndex(list *list, long index) {
    listNode *n;
	//如果index<0先将其转换为正数
    if (index < 0) {
    	//这里减一是因为,从尾节点找到第一个元素无需移动只需返回尾节点即可;找到第二个元素只需从尾节点向前移动一次即可
        index = (-index)-1;
        n = list->tail;
        while(index-- && n) n = n->prev;
    } else {
        n = list->head;
        while(index-- && n) n = n->next;
    }
    return n;
}

redis3.2.100quicklist

在redis3.2.100版本中quicklist是由“ziplist”组成的"linkedlist"

quicklist.c - A doubly linked list of ziplists

看一下它的结构

/* quicklistNode is a 32 byte struct describing a ziplist for a quicklist.
 * We use bit fields keep the quicklistNode at 32 bytes.
 * count: 16 bits, max 65536 (max zl bytes is 65k, so max count actually < 32k).
 * encoding: 2 bits, RAW=1, LZF=2.
 * container: 2 bits, NONE=1, ZIPLIST=2.
 * recompress: 1 bit, bool, true if node is temporarry decompressed for usage.
 * attempted_compress: 1 bit, boolean, used for verifying during testing.
 * extra: 12 bits, free for future use; pads out the remainder of 32 bits */
/*
quicklistnode是“由ziplist组成的linkedlist”的节点,占有32bytes
*/
typedef struct quicklistNode {
    struct quicklistNode *prev;
    struct quicklistNode *next;
    unsigned char *zl;
    unsigned int sz;             /* ziplist size in bytes ziplist占用的bytes*/
    unsigned int count : 16;     /* count of items in ziplist  ziplist里的entry数量*/
    unsigned int encoding : 2;   /* RAW==1 or LZF==2 */
    unsigned int container : 2;  /* NONE==1 or ZIPLIST==2 */
    unsigned int recompress : 1; /* was this node previous compressed? */
    unsigned int attempted_compress : 1; /* node can't compress; too small */
    unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;
/* quicklist is a 32 byte struct (on 64-bit systems) describing a quicklist.
 * 'count' is the number of total entries.
 * 'len' is the number of quicklist nodes.
 * 'compress' is: -1 if compression disabled, otherwise it's the number
 *                of quicklistNodes to leave uncompressed at ends of quicklist.
 * 'fill' is the user-requested (or default) fill factor. */
typedef struct quicklist {
    quicklistNode *head;
    quicklistNode *tail;
    unsigned long count;        /* total count of all entries in all ziplists 所有ziplist中的所有entry总和,也就是linkedlist中总的元素个数 */
    unsigned int len;           /* number of quicklistNodes  quicklistnode的个数*/
    int fill : 16;              /* fill factor for individual nodes */
    unsigned int compress : 16; /* depth of end nodes not to compress;0=off */
} quicklist;

redis7.2.2quicklist

在7.2.2版本中quicklist是由“listpack组成的”

quicklist.c - A doubly linked list of listpacks

看一下它的结构

/* quicklistNode is a 32 byte struct describing a listpack for a quicklist.
 * We use bit fields keep the quicklistNode at 32 bytes.
 * count: 16 bits, max 65536 (max lp bytes is 65k, so max count actually < 32k).
 * encoding: 2 bits, RAW=1, LZF=2.
 * container: 2 bits, PLAIN=1 (a single item as char array), PACKED=2 (listpack with multiple items).
 * recompress: 1 bit, bool, true if node is temporary decompressed for usage.
 * attempted_compress: 1 bit, boolean, used for verifying during testing.
 * extra: 10 bits, free for future use; pads out the remainder of 32 bits */
typedef struct quicklistNode {
    struct quicklistNode *prev;
    struct quicklistNode *next;
    unsigned char *entry;
    size_t sz;             /* entry size in bytes  listpack占用bytes*/
    unsigned int count : 16;     /* count of items in listpack  listpack中的元素个数*/
    unsigned int encoding : 2;   /* RAW==1 or LZF==2 */
    unsigned int container : 2;  /* PLAIN==1 or PACKED==2 */
    unsigned int recompress : 1; /* was this node previous compressed? */
    unsigned int attempted_compress : 1; /* node can't compress; too small */
    unsigned int dont_compress : 1; /* prevent compression of entry that will be used later */
    unsigned int extra : 9; /* more bits to steal for future usage */
} quicklistNode;
/* quicklist is a 40 byte struct (on 64-bit systems) describing a quicklist.
 * 'count' is the number of total entries.
 * 'len' is the number of quicklist nodes.
 * 'compress' is: 0 if compression disabled, otherwise it's the number
 *                of quicklistNodes to leave uncompressed at ends of quicklist.
 * 'fill' is the user-requested (or default) fill factor.
 * 'bookmarks are an optional feature that is used by realloc this struct,
 *      so that they don't consume memory when not used. */
typedef struct quicklist {
    quicklistNode *head;
    quicklistNode *tail;
    unsigned long count;        /* total count of all entries in all listpacks  linkedlist的元素总数*/
    unsigned long len;          /* number of quicklistNodes quicklistnode的数量 */
    signed int fill : QL_FILL_BITS;       /* fill factor for individual nodes */
    unsigned int compress : QL_COMP_BITS; /* depth of end nodes not to compress;0=off */
    unsigned int bookmark_count: QL_BM_BITS;
    quicklistBookmark bookmarks[];
} quicklist;

总结

  • 不管是ziplist还是listpack构成的quicklist获取元素总数的时间复杂度为都为O(1)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1425933.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【代码随想录】LC 77. 组合

文章目录 前言一、题目1、原题链接2、题目描述 二、解题报告1、思路分析2、代码详解 前言 本专栏文章为《代码随想录》书籍的刷题题解以及读书笔记&#xff0c;如有侵权&#xff0c;立即删除。 一、题目 1、原题链接 77. 组合 2、题目描述 二、解题报告 1、思路分析 &#x…

C++类和对象(中)六个默认成员函数

&#x1f308;类的六个默认成员函数 任何一个类&#xff0c;不管是否为空&#xff0c;都会在生成的时候默认调用六个成员函数&#xff0c;这些成员函数可以自动生成&#xff0c;也可以由程序员写出。这六个默认成员函数分别是&#xff1a; 最主要的是前四个&#xff1a; 初始…

2-树-恢复二叉搜索树

这是树的第二篇算法&#xff0c;力扣链接。 给你二叉搜索树的根节点 root &#xff0c;该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下&#xff0c;恢复这棵树 。 示例 1&#xff1a; 输入&#xff1a;root [1,3,null,null,2] 输出&#xff1a;[3,1,null,…

Google Gemini Pro 国内版

Google Gemini Pro 国内版&#xff1a;【直达链接】 Google Gemini Pro 国内版 能力分类基准测试描述更高分数更好Gemini UltraGPT-4通用MMLU57个主题&#xff08;包括STEM、人文等&#xff09;的问题表示是90.0%86.4%&#xff08;5-shot, 报告&#xff09;推理Big-Bench Hard…

linux系统上C程序的编译、运行及调试-gcc

gcc -o timer timer.c &#xff1a;生成可执行文件main&#xff0c;依托main.c,也可依托多个文件./timer :运行代码

C# 根据USB设备VID和PID 获取设备总线已报告设备描述

总线已报告设备描述 DEVPKEY_Device_BusReportedDeviceDesc 模式 winform 语言 c# using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Window…

【软件设计师笔记】计算机系统基础知识考点

&#x1f413; 计算机系统组成 计算机系统是由硬件和软件组成的&#xff0c;它们协同工作来运行程序。计算机的基本硬件系统由 运算器、控制器、存储器、输入设备和输出设备5大部件组成。运算器、控制器等部件被集成 在一起统称为中央处理单元&#xff08;Central Processing …

幻兽帕鲁服务器怎么更新?教你一行命令搞定!

今天发现幻兽帕鲁又进行了一次更新&#xff0c;所以我们的服务器也是需要同步更新的 那么&#xff0c;我们通过阿里云或腾讯云一键部署的幻兽帕鲁服务器怎么更新呢&#xff1f; 腾讯云轻量云一键部署幻兽帕鲁服务器教程&#xff1a;https://curl.qcloud.com/pzBO9wN7 先来讲…

短视频去水印教程,免费一键获取视频、图片、文案【迅风去水印】

自媒体行业的蓬勃发展&#xff0c;让越来越多的创作者涌入其中。然而&#xff0c;剪辑过程中常常遭遇到一个令人头疼的问题&#xff0c;那就是视频或图片上的水印。这些水印不仅会影响到作品的美感&#xff0c;还可能侵犯到版权。为了帮大家解决这一难题&#xff0c;分享一个免…

HTML 样式学习手记

HTML 样式学习手记 在探索网页设计的世界时&#xff0c;我发现HTML元素的样式调整真的是个很酷的环节。通过简单的属性设置&#xff0c;就能让文字换上五彩斑斓的颜色、变换各异的字体和大小。特别是那个style属性&#xff0c;感觉就像是一扇通往CSS魔法世界的大门。 代码小试…

uniapp微信小程序触底加载(超简单)

你在哪个页面需要就给他在page.json里面填写以下代码&#xff0c;表示距离底部还有50px就触发 1.page.json添加以下代码 "onReachBottonDistance":50 这是文档链接 页面 | uni-app官网 (dcloud.net.cn) 2. 页面中写以下代码 onReachBottom(e) {console.log(&quo…

【Git】01 Git介绍与安装

文章目录 一、版本控制系统二、Git三、Windows安装Git3.1 下载Git3.2 安装3.3 检查 四、Linux安装Git4.1 YUM安装4.2 源码安装 五、配置Git5.1 配置用户名和邮箱5.2 配置级别5.3 查看配置 六、总结 一、版本控制系统 版本控制系统&#xff0c;Version Control System&#xff…

Rhino 8.1下载安装教程,保姆级教程,小白也能轻松搞定,附安装包和工具

前言 Rhinoceros,又叫犀牛&#xff0c;是一款小巧强大的高级三维建模工具&#xff0c;适用于机械设计、科学工业、三维动画等广泛领域。它包含了所有的NURBS建模功能&#xff0c;用它建模感觉非常流畅&#xff0c;所以大家经常用它来建模&#xff0c;然后导出高精度模型给其他…

ElementUI Form:Input 输入框

ElementUI安装与使用指南 Input 输入框 点击下载learnelementuispringboot项目源码 效果图 el-input.vue &#xff08;Input 输入框&#xff09;页面效果图 项目里el-input.vue代码 <script> export default {name: el_input,data() {return {input: ,input1: ,i…

了解 WebSocket 和 TCP :有何不同

WebSocket — 双向通讯的艺术 简要概述 WebSocket 代表着WebSocket通讯协议&#xff0c;提供了一条用于客户端和服务器间实现实时、双向、全双工通信的渠道。在WebSocket引入之前&#xff0c;网页应用的数据更新依赖于频繁的轮询&#xff0c;这种做法不仅效率低下&#xff0c;…

上位机是什么?与下位机是什么关系

在工业自动化领域中&#xff0c;上位机是一项关键而引人注目的技术。许多人对上位机的概念感到好奇&#xff0c;想要深入了解其在工业智能中的作用。那么&#xff0c;上位机究竟是什么呢&#xff1f; 首先&#xff0c;上位机是一种用于工业控制系统的软件应用&#xff0c;通常…

elementUI中表单校验的清空校验以及手动校验

this.$refs.表单.clearValidate(),这个可以传入字符串或者字符串数组&#xff0c;字符串对应的是我们自定义的rule里面的属性名&#xff0c;rule的属性名对应的是el-form-item的prop。这个api目前遇到的场景是el-radio切换时v-if展示不同的表单内容&#xff0c;但是当有校验提示…

把成绩私发给家长

与家长保持及时、有效的沟通对于学生的成长至关重要。但有时候&#xff0c;我会选择将学生的成绩私发给家长&#xff0c;而不是在公共场合公布。这样做有以下几个原因。 保护学生的隐私。每个学生都拥有自己的个人信息&#xff0c;这包括学习成绩。在公共场合公布成绩&#xf…

前后端项目

文章目录 1.需求2.项目搭建2.1项目结构图2.2构建聚合工程2.2.1 zx-parent父工程2.2.2 zx-framework父工程2.2.2.1 zx-common工程2.2.2.2 zx-mybatisplus工程2.2.3 如上,同理创建其他父子工程2.3准备sql3.用户登录3.1 修改pom-依赖其他Module3.2 封装User1.需求 在线学习平台 …

vue3-深入组件-插槽

插槽 Slots 组件用来接收模板内容 插槽内容与出口 <slot> 元素是一个插槽出口 (slot outlet),&#xff0c;标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。 插槽内容可以是任意合法的模板内容&#xff0c;不局限于文本。例如我们可以传入多个元素&#xff0…