Redis学习之链表

文章详细介绍了Redis中链表的应用,如list命令的使用,以及如何模拟队列和栈。同时,文章深入讲解了Redis链表的内部实现,包括listNode和list结构,以及相关操作的时间复杂度。此外,还展示了链表节点插入到表头和表尾的源码实现。

C本系列是个人的Redis学习笔记,参考的书籍有《Redis设计与实现》、《Redis深度历险–核心原理与应用实践》,Redis源码版本是黄健宏老师注释的Redis-3.0-annotated版本

一、Redis中的链表应用场景

链表是一种应用非常广泛的数据结构,在很多语言中都有相应的实现和使用,比如Java中的LinkedList。C语言中并没有内置的链表实现,因此Redis实现了自己的链表结构,并将它应用在许多场景,比如我们在Redis中使用的list,其底层就是用链表实现的。

Redis中的list是由字符串值组成的双向链表,其基本用法如下:

127.0.0.1:6379> lpush fruits apple banana orange
(integer) 3

Redis中的list支持从两边对链表进行操作,其主要使用的命令有LPUSH,RPUSH,LPOP,RPOP。

利用这些命令,可以把list作为队列或者栈来使用。

比如组合使用LPUSH和RPOP,即左进右出,那么可以模拟成队列。当然也可以反过来,RPUSH和LPOP,即右进左出。比如像下面这样使用:

127.0.0.1:6379> lpush fruits apple banana orange
(integer) 3
127.0.0.1:6379> rpop fruits
"apple"

二、Redis的链表实现

Redis中的链表实现与常见的链表差不多,基本结构有listNode和list。listNode是链表节点结构,list是链表结构。listNode的基本结构如下:

typedef struct listNode {

    // 前置节点
    struct listNode *prev;
    // 后置节点
    struct listNode *next;
    // 节点的值
    void *value;

} listNode;

next指针作为前向指针,可以沿着表头到表尾的方向进行遍历;prev指针作为后向指针,可以沿着表尾到表头的方向进行遍历;value是一个值指针。

使用listNode组成的双向链表结构如下所示:

list的结构如下所示:

typedef struct list {

    // 头节点
    listNode *head;
    // 尾节点
    listNode *tail;
    // 链表长度,即链表所包含的节点数
    unsigned long len;
    // 节点值复制函数
    void *(*dup)(void *ptr);
    // 节点值释放函数
    void (*free)(void *ptr);
    // 节点值对比函数
    int (*match)(void *ptr, void *key);

} list;

通过list 结构可以很方便地取到链表的头节点、尾节点以及链表长度。dup 、 free 和 match 属性是用于实现多态链表所需的类型特定函数,具体地,dup 函数用于复制链表节点所保存的值;free 函数用于释放链表节点所保存的值;match 函数则用于对比链表节点所保存的值和另一个输入值是否相等。使用list之后的链表结构如下:

Redis 的链表实现的特性可以总结如下:

  • 双端: listNode有 prev 和 next 指针, 可以以 O ( 1 ) O(1) O(1)的时间复杂度获取某个节点的前置节点和后置节点
  • 无环: 表头节点的 prev 指针和表尾节点的 next 指针都为 NULL , 对链表的访问以 NULL 为终点
  • 带表头指针和表尾指针: 通过 list的 head 指针和 tail 指针可以很容易获取表头结点和表尾节点
  • O ( 1 ) O(1) O(1)时间复杂度获取链表长度:list 的len 属性来记录了链表节点
  • 多态: listNode使用 void* 指针来保存节点值, 并且可以通过 list 的 dup 、free 、match 三个属性为节点值设置类型特定函数, 所以链表可以用于保存各种不同类型的值并且应用不同的复制函数、内存释放函数和匹配函数。

三、list中一些函数的源码实现

  1. 将元素插入到list表头,如下图所示,向表头插入val0之后list的变化,红色的相关指针是发生变化的指针。

/*
 * 使用给定的value值构造新的节点并添加到链表的表头
 * 如果为新节点分配内存出错,那么不执行任何动作,仅返回 NULL
 * 如果执行成功,返回传入的链表指针
 * T = O(1)
 */
list *listAddNodeHead(list *list, void *value)
{
    listNode *node;

    // 为节点分配内存
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;

    // 保存值指针
    node->value = value;

    // 添加节点到空链表
    // 当链表为空时,新插入的节点既是头结点也是尾结点
    if (list->len == 0) {
        list->head = list->tail = node;
        // 注意头结点的前驱指针应该为空,尾结点的后继指针应该为空
        node->prev = node->next = NULL;
    // 添加节点到非空链表
    // 当链表非空时,采用头插法将新插入节点链接到链表头部,并更新list的head指针
    } else {
        node->prev = NULL; //注意头结点的前驱指针应该为空
        node->next = list->head;
        list->head->prev = node;
        list->head = node;
    }

    // 更新链表节点数
    list->len++;

    return list;
}
  1. 将元素插入到list表尾,如下图所示,想表尾插入val5之后list的变化,红色的相关指针是发生变化的指针。

/*
 * 使用给定的value值构造新的节点并添加到链表的表尾
 * 如果为新节点分配内存出错,那么不执行任何动作,仅返回 NULL
 * 如果执行成功,返回传入的链表指针
 * T = O(1)
 */
list *listAddNodeTail(list *list, void *value)
{
    listNode *node;

    // 为新节点分配内存
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;

    // 保存值指针
    node->value = value;

    // 目标链表为空
    // 当链表为空时,新插入的节点既是头结点也是尾结点
    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;
    }

    // 更新链表节点数
    list->len++;

    return list;
}
  1. 创建list,如下图所示,其中NULL指针没有画出:

/*
 * 创建一个新的链表
 * 创建成功返回链表,失败返回 NULL 。
 * T = O(1)
 */
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;
}
安全光幕也就是光电安全保护装置(也称安全保护器、冲床保护器、红外线安全保护装置等)。主要应用在人与机器协同工作,在一些具有潜在危险的机械设备上,如冲压机械、剪切设备、金属切削设备、自动化装配线等,用于保护作业人员的安全。现在的安全光幕产品主要分为直线扫描方式和交叉扫描方式两种,本设计采用了直线扫描方式。安全光幕通常由红外发射器和红外接收器两部分组成。安全光幕主要技术参数如下:电源: 直流24V保护高度: 140mm检测距离: 1~2000mm(通过配置参数可调节测试距离)光束数量: 8光轴间距: 20mm分辨率: 15mm响应时间: 10ms输出类型: NPN安全光幕红外发射器电路板正面如图:安全光幕红外接收器电路板正面如图:安全光幕原理介绍:安全光幕由红外发射装置、红外接收装置以及以MCU 为核心的电子电路组成。光幕的发送端等间距安装有多个红外发射管,接收端相应的有相同数量同样排列的红外接收管,每一个红外发射管都对应有一个红外接收管,且安装在同一水平线上。当同一水平线上的红外发射管、红外接收管之间没有障碍物时,红外发射管发出的红外光调制信号能顺利到达红外接收管。而在有障碍物的情况下,红外发射管发出的红外光调制信号则不能顺利到达红外接收管,这时该红外接收管接收不到红外光调制信号,相应的输出到外部继电器的控制信号输出有效电平,控制设备停止或断电,从而达到保护的目的。安全光幕探测原理如图所示。红色线条代表红外线,红色线条覆盖区域是被保护的区域。左图是保护区域内无物体的情况,右图是保护区域内有物体的情况。更多详细说明,详见附件内容,附件内容截图:
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值