四、哈希键函数 1.获取指定字段的值 2.获取哈希表容量 五、集合键函数 1.向集合添加元素 2.判断元素是否在集合内部 六、有序集合键函数 1.从有序集合删除元素 2.获取指定元素分值 总结 一、Redis客户端结构体简介 Redis为每一个客户端定义了redisClient 对象,包括客户端套接字,客户端输入的命令参数数目,和参数数组等。 = REDIS_OK) return; // expire 参数的值不正确时报错 if (milliseconds <= 0) { 1.获取指定字段的值 hget key field命令可可以获取指定字段的值,实现函数如下:其中hgetCommand函数调用addHashFieldToReply函数,addHashFieldToReply * * 参数: * field 域 * vstr 值是字符串时,将它保存到这个指针 * vlen 保存字符串的长度 * ll 值是整数时,将它保存到这个指针
,保存集合数据 int8_t contents[]; } intset; contents是整数数组底层实现,用来存储元素,并且各个项在数组中的值按从小到大有序排列,并且数组中不包含重复元素。 index元素的起始地址 IntSet升級 升级编码为INTSET_ENC_INT32,每个整数占4字节,并按照新的编码方式及元素个数扩容数组 倒序依次将数组中的元素拷贝到扩容后的正确位置 正序挨个拷贝 ,数组中保存的是指向entry的指针 dictEntry **table; //哈希表的大小 unsigned long size; //哈希表大小的掩码,总是等于size 小结 ZipList(压缩列表) ZipList是一种特殊的"双端链表",由一系列特殊编码的连续内存块组成。可以在任意一端进行压入/弹出操作,并且该操作的时间复杂度为0(1)。 值一样则按照ele字典排序 每个节点都可以包含多层指针,层数是1到32之间的随机数 不同层指针到下一个节点的跨度不同,层级越高,跨度越大 增删改成效率与红黑树基本一致,实现却更为简单 RedisObject
领8888元新春采购礼包,抢爆款2核2G云服务器95元/年起,个人开发者加享折上折
, 将各个 obj 指针所指向的列表项转换成一个 double 类型的浮点数, 并将这个浮点数保存在相应数组项的 u.score 属性里面, 如下图所示: d.根据数组项 u.score 属性的值 , 对数组进行数字值排序, 排序后的数组项按 u.score 属性的值从小到大排列, 如下图所示: d.遍历数组, 将各个数组项的 obj 指针所指向的列表项作为排序结果返回给客户端: 程序首先访问数组的索引 b.遍历数组, 将各个数组项的 obj 指针分别指向 str集合的各个项, 构成 obj 指针和集合元素之间的一对一关系。 b.遍历数组, 将各个数组项的 obj 指针分别指向 grade 集合的各个项, 构成 obj 指针和集合元素之间的一对一关系。 d.将查找的权重键的值转换成double类型的浮点数,然后保存在对应数组项的u.score属性中。 f.遍历数组, 将各个数组项的 obj 指针所指向的集合元素作为排序结果返回给客户端。
【2】为什么会有int,因为整型值最大固定是64bit,其实与指针*ptr占据的大小一致,其实把数值存于这里可以减少了对空间的开辟。 // 注意ptr字段本来是一个void *指针(即存储的是内存地址), // 因此在64位机器上有64位宽度,正好能存储一个64位的long型值。 _t)))) //获取ziplist的zltail的指针 #define ZIPLIST_LENGTH(zl) (*((uint16_t*)((zl)+sizeof(uint32_t)*2)) 1.采用双指针的方式,那就必须赋予两个指针pre和next,一个指针占据了8byte,故两个指针就需要消耗16byte。 2.采用双链表的话数据可能会分的很散,因为指针就是采用不连续的存储空间来存储数据,容易造成大量的内存碎片。
思路和代码 这是一个简单的双指针问题,即左指针指向可以作为起点的子数组下标,右指针则不停向右移动,将更多的元素囊括进来,从而确保该左右指针内的元素是目标数组的一个兄弟子数组(即每个字母的个数均相等) c : p.toCharArray()) { map[c-'a']++; } char[] sArray = s.toCharArray if(tmpMap[index] > 0) { tmpMap[index]--; //如果左右指针中数组的长度等于目标数组长度 } }else if(sArray[startIndex] == sArray[i]){//如果左右指针值相等,则左指针只向右移动一个 startIndex++; }else { //将左指针移动到当前右指针的位置上,因为中间部分的子数组一定无法构成目标数组
聪明的人总是有,有人想出了用数组来代替指针,来描述单链表,让每个数组的元素都由两个数据域组成,数组的每个下标都对应两个数据域,一个用来存放数据元素,一个用来存放next指针。 0,i<L.length,++i) //遍历L长度中的每个位置 if(e == L.data[i]) //获取每个位置对应的值和e值进行判断,这里的等于可以是大于、 //插入成功,返回1 } 3.删除数据元素算法 将顺序表的第p个位置的元素e进行删除,如果p的输入不正确,则返回0,代表删除失败;如果p的输入正确,则将顺序表中位置p后面的元素依次往前传递,把位置p的元素覆盖掉即可 //更新指针r的指向 } r -> next = NULL; //直到r指针指向为NULL } 2.查找结点的算法 在双链表中查找值为 x的结点,如果找到,则返回该结点的指针,否则返回NULL值。
** # 解题思路 方法1、排序比较: 先给数组排序,然后进行一一比较,遇到不相等的位置就更新start和end start始终靠最小值比较,end始终靠最大值比较 之后就能够通过end-start+1 得到未排序子数组的长度 特例判断:只有当end-start+1>=0时,长度计算有效,否则为0 方法2、双指针找边界: 初步思路是,使用双指针,指针i从头开始遍历,指针j从尾开始遍历。 于是换一种思路,让指针分别找到最后逆序的位置 同时从前往后和从后往前遍历,分别得到要排序数组的右边界和左边界; 寻找右边界: 从前往后遍历的过程中,用max记录遍历过的最大值,如果max大于当前的nums [i],说明nums[i]的位置不正确,属于需要排序的数组,因此将右边界更新为i,然后更新max;这样最终可以找到需要排序的数组的右边界,右边界之后的元素都大于max; 寻找左边界: 从后往前遍历的过程中 ,用min记录遍历过的最小值,如果min小于当前的nums[j],说明nums[j]的位置不正确,应该属于需要排序的数组,因此将左边界更新为j,然后更新min;这样最终可以找到需要排序的数组的左边界,左边界之前的元素都小于
list *clients; /* List of active clients */ ........ } clients 字段是一个双端链表结构,保存了所有成功建立连接的客户端 二、redis 如何执行一条命令 redis 服务端程序启动后,会初始化一些字段变量,为 redisServer 中的一些字段赋默认值,还会读取用户指定的配置文件内容并加载配置,反应到具体数据结构内,最后会调用 //根据不同的请求类型,执行命令解析 //实际上就是把命令的名称、参数解析存入 argc 数组中 if (c->reqtype == PROTO_REQ_INLINE) timeEventHead 是一个双端链表,所有的时间事件都会以链表的形式存储在这里,具体指向的结构是 aeTimeEvent。 //获取当前时间 aeGetTime(&now_sec, &now_ms); if (now_sec > te->when_sec || (now_sec == te->when_sec
首先检查此命令的格式是否正确,如果不正确,服务器会在客户端状态(redisClient)的 flags 属性打开 REDIS_MULTI 标识,并且返回错误信息给客户端。 如果正确将这个命令放入一个事务队列里面,然后向客户端返回 QUEUED 回复。 我们先看看事务队列是如何实现的? ,数组中每个 multiCmd 结构都保存了一个如入队命令的相关信息:指向命令实现函数的指针,命令的参数,以及参数的数量。 * 事务命令 */ typedef struct multiCmd { // 参数 robj **argv; // 参数数量 int argc; // 命令指针 MySQL 的 SQL 查询是可以相当复杂的,而且 MySQL 没有事务队列这种说法,SQL 真正开始执行才会进行分析和检查,MySQL 不可能提前知道下一条 SQL 是否正确。
本文主要介绍「逆向双指针」的策略来解答此题,供大家参考,希望对大家有所帮助。 ❞ ❝ 策略二:双指针法,先开辟一个新数组,长度为两个数组的长度之和,然后让两个指针分别指向两个数组的头部,比较这个两个指针指向的数组元素的值,将数值较小的放到新数组的头部,再将指向的数值较小的指针右移 【空间复杂度】:策略一是「O(1)」,未开辟额外的存储空间;策略二是「O((n + m))」,额外开辟了长度为 m + n 的数组。 逆向双指针 从前面的分析可知,策略二相对于策略一来说,时间复杂度「更优」了,但开辟了「额外」的空间。 有没有方法能够将策略二的空间复杂度优化呢? p 和 q 指向数组位置的元素值 image.png 将元素值较大的存放在 nums1[k] 中,并左移 k 和 q(指向的数值较大的指针) image.png 以此类推,其处理的完整过程如下动图示
对前面未反转(left之前)的链表处理过于暴力。 理解(不保证正确) 1,链表题中出现左右边界值问题,引入一个虚拟头指针,能避免大量问题。 3,相交链表 来自 LeetCode160 解法 1,双指针的智商题 其实就是走两次,经常能用到这个思想 /** * Definition for singly-linked list. 2,正向双指针 因为已经排好序,所以比较最小只需要比较最靠前的元素即可。 这里就能想到双指针,但是这个方法必须新建一个新数组,空间复杂度高。 正向双指针的逆想法 如果不想新建新数组的话,用最小覆盖可能会覆盖nums1原来的元素 所以我们可以想到用最大来覆盖,nums1最后的元素,因为nums1长度固定。
存放了列表键、哈希键和字符串键的键空间如下所示: 所以,每次键空间新建键、获取键的值、更新键内容、删除键的操作均是调用词典的API实现。 6.数据库通知 Redis客户端可以订阅给定的频道或者模式,来从数据库获取通知。数据库通知分为两种,键空间通知和键事件通知。 key is expired at this time. */ // 当服务器运行在 replication 模式时 // 附属节点并不主动删除 key // 它只返回一个逻辑上正确的返回值 ) return REDIS_ERR; // 切换数据库(更新指针) c->db = &server.db[id]; return REDIS_OK; } void 的值存在,那么返回该值;否则,返回 NULL 。
要求: 删除有序数组(或有序单链表)中的重复项。 示例: 输入[1,1,2,2,3] 输出[1,2,3] 输入a->b->b->c->c 输入a->b->c 思路: 双指针,慢指针从第1个有效元素开始,快指针从第2个有效元素开始,快指针对应的元素与慢指针对应的元素比较 跟数组不同的是,当fast到达末节点时,slow的next必须设置为空,否则如果末端的几个节点出现重复时,尾巴上的重复节点甩不掉。 ,可参考上一篇 扩展:如果要去重的数组,不是有序的,比如['a','a','b','a','c','c'] 这种如何去重呢? 仍然可以用双指针法,但是每次fast指针对应的元素,就必须再到慢指针之前的所有元素中,对比一次,才能知道是不是重复了。
数组列表底层是使用数组存储值,我们可以通过索引在 O(1) 的时间访问列表任何位置的值,这是由基于内存寻址的方式。 链表存储的是称为节点的对象,每个节点保存一个值和指向下一个节点的指针。 访问某个特定索引的节点需要 O(n)的时间,因为要通过指针获取到下一个位置的节点。 确定数组列表是否回文很简单,我们可以使用双指针法来比较两端的元素,并向中间移动。 然而同样的方法在链表上操作并不简单,因为不论是正向访问还是反向访问都不是 O(1)。而将链表的值复制到数组列表中是 O(n),因此最简单的方法就是将链表的值复制到数组列表中,再使用双指针法判断。 算法 一共为两个步骤: 复制链表值到数组列表中。 使用双指针法判断是否为回文。 第一步,我们需要遍历链表将值复制到数组列表中。我们用 currentNode 指向当前节点。 第一步: 遍历链表并将值复制到数组中,O(n)。 第二步:双指针判断是否为回文,执行了 O(n/2)次的判断,即 O(n)。 总的时间复杂度:O(2n) = O(n)。
预空间分配:如果对一个SDS进行修改,分为以下两种情况: SDS长度(len的值)小于1MB,那么程序将分配和len属性同样大小的未使用空间,这时free和len属性值相同。 举个例子,SDS的len将变成15字节,则程序也会分配15字节的未使用空间,SDS的buf数组的实际长度变成15+15+1=31字节(额外一个字节用户保存空字符)。 SDS长度(len的值)大于等于1MB,程序会分配1MB的未使用空间。比如进行修改之后,SDS的len变成30MB,那么它的实际长度是30MB+1MB+1byte。 从图中可以看出Redis的linkedlist双端链表有以下特性:节点带有prev、next指针、head指针和tail指针,获取前置节点、后置节点、表头节点和表尾节点的复杂度都是O(1)。 len属性获取节点数量也为O(1)。 与双端链表相比,压缩列表可以节省内存空间,但是进行修改或增删操作时,复杂度较高;因此当节点数量较少时,可以使用压缩列表;但是节点数量多时,还是使用双端链表划算。
预空间分配:如果对一个SDS进行修改,分为一下两种情况: SDS长度(len的值)小于1MB,那么程序将分配和len属性同样大小的未使用空间,这时free和len属性值相同。 举个例子,SDS的len将变成15字节,则程序也会分配15字节的未使用空间,SDS的buf数组的实际长度变成15+15+1=31字节(额外一个字节用户保存空字符)。 SDS长度(len的值)大于等于1MB,程序会分配1MB的未使用空间。比如进行修改之后,SDS的len变成30MB,那么它的实际长度是30MB+1MB+1byte。 从图中可以看出Redis的linkedlist双端链表有以下特性:节点带有prev、next指针、head指针和tail指针,获取前置节点、后置节点、表头节点和表尾节点的复杂度都是O(1)。 len属性获取节点数量也为O(1)。 与双端链表相比,压缩列表可以节省内存空间,但是进行修改或增删操作时,复杂度较高;因此当节点数量较少时,可以使用压缩列表;但是节点数量多时,还是使用双端链表划算。
,nums[i]每次减minS,假设minS初始化为其他值,那么可能出现跳过第一个值或者初始值不在数组中的情况 674. ,当然昨天的暴力解法也是正确的。 : 从0到1入门双指针 入门是不够的,下面我们来看双指针的三种情况: 数组相向追赶 数组相向逼近 链表快慢指针(有点难) 数组相向追赶 俩个指针,i可以一直往前走,但是j只有当满足条件的时候才往前走 数组相向逼近 一般来说,俩个指针从数组的俩端开始,不断的去check是否满足条件,根据不同的条件,来选择是左指针自增,还是右指针自减 链表快慢指针 快慢指针,顾名思义,定义俩个指针,一个指针可以走的很快 ,另一个相对走的较慢,当快指针走到链表结尾,慢指针对应的节点,获取一些信息,从而解决一些问题。
一、二进制位数组 1.二进制位数组命令 Redis提供了setbit getbit bitcount和bitop四个命令用于处理二进制位数组,如下所示: //设置某一位的值 redis>setbit bit 0 1 //0000 0001 redis>setbit bit 3 1 //0000 1001 //获取某一位的值 redis>getbit bit 0 1 redis>getbit bit c.根据 byte 值和 bit 值,在位数组找到 offset 偏移量指定的二进制位。 = REDIS_OK) return; // 获取 value 参数 if (getLongFromObjectOrReply(c,c->argv[3],&on,err +1); /* Get current values */ // 将指针定位到要设置的位所在的字节上 byteval = ((uint8_t*)o->ptr)[byte];
扫码关注腾讯云开发者
领取腾讯云代金券