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\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 能实现栈
从相反方向推入和弹出 如:lpush和rpop能实现队列
Redis list实现原理 - 双向循环链表的更多相关文章
- (C语言版)链表(四)——实现双向循环链表创建、插入、删除、释放内存等简单操作
双向循环链表是基于双向链表的基础上实现的,和双向链表的操作差不多,唯一的区别就是它是个循环的链表,通过每个节点的两个指针把它们扣在一起组成一个环状.所以呢,每个节点都有前驱节点和后继节点(包括头节点和 ...
- 一种神奇的双向循环链表C语言实现
最近在看ucore操作系统的实验指导.里面提要一个双向循环链表的数据结构,挺有意思的. 其实这个数据结构本身并不复杂.在普通链表的基础上加一个前向指针,我们就得到了双向链表,再把头尾节点连起来就是双向 ...
- 一、Redis基本操作——String(原理篇)
小喵的唠叨话:最近京东图书大减价,小喵手痒了就买了本<Redis设计与实现>[1]来看看.这里权当小喵看书的笔记啦.这一系列的模式,主要是先介绍Redis的实现原理(可能很大一部分会直接照 ...
- 双向链表、双向循环链表的JS实现
关于链表简介.单链表.单向循环链表.JS中的使用以及扩充方法: 单链表.循环链表的JS实现 关于四种链表的完整封装: https://github.com/zhuwq585/Data-Structu ...
- C语言通用双向循环链表操作函数集
说明 相比Linux内核链表宿主结构可有多个链表结构的优点,本函数集侧重封装性和易用性,而灵活性和效率有所降低. 可基于该函数集方便地构造栈或队列集. 本函数集暂未考虑并发保护. 一 ...
- 双向循环链表的Java版本实现
1.单项循环列表 单向循环链表是单链表的另一种形式,其结构特点是链表中最后一个结点的指针不再是结束标记,而是指向整个链表的第一个结点,从而使单链表形成一个环.和单链表相比,循环单链表的长处是从链尾到链 ...
- c语言编程之双向循环链表
双向循环链表就是形成两个环,注意每个环的首尾相连基本就可以了. 程序中采用尾插法进行添加节点. #include<stdio.h> #include<stdlib.h> #de ...
- Linux内核中的通用双向循环链表
开发中接触Linux越来越多,休息放松之余,免不了翻看翻看神秘的Linux的内核.看到双向链表时,觉得挺有意思的,此文记下. 作为众多基础数据结构中的一员,双向循环链表在各种“教科书”中的实现是相当的 ...
- Redis压缩列表原理与应用分析
摘要 Redis是一款著名的key-value内存数据库软件,同时也是一款卓越的数据结构服务软件.它支持字符串.列表.哈希表.集合.有序集合五种数据结构类型,同时每种数据结构类型针对不同的应用场景又支 ...
随机推荐
- TPO4-2 Cave Art in Europe
Perhaps, like many contemporary peoples, Upper Paleolithic men and women believed that the drawing o ...
- POJ-3264 Balanced Lineup(区间最值,线段树,RMQ)
http://poj.org/problem?id=3264 Time Limit: 5000MS Memory Limit: 65536K Description For the daily ...
- 2019-2020-1 20199324《Linux内核原理与分析》第三周作业
第二章 操作系统是如何工作的 一.知识点总结 1.计算机的三个法宝 存储程序计算机 函数调用堆栈机制.堆栈:是C语言程序运行时必须使用的记录函数调用路径和参数存储的空间. 中断 2.堆栈相关的寄存器和 ...
- 74cms_3.5.1 宽字节注入
第一次进行CMS的代码审计,我选择了2014年发布的74CMS 3.5.1,历史比较久远的CMS往往存在更多的问题,虽然技术上难度不大,但是在思路方面给了我很大的启发.下面我根据我的思路给大家分享一下 ...
- java正则(贪婪、勉强)
贪婪.勉强和侵占量词间的不同 在贪婪.勉强和侵占三个量词间有着细微的不同. 贪婪(*, ?, +):读入整个串,从后往前匹配 勉强(*?, ??, +?):从前往后匹配 侵占(*+, ?+, ++): ...
- jsp页面之间传中文参数显示乱码问题的解决
最近在项目中遇到jsp页面通过url传递参数,出现乱码,但是在本地是正常显示,在服务器上却是乱码,找了好久都没找到解决方法,最终在大神的帮助下解决了这个问题 比如从a.jsp像b.jsp页面传递参数 ...
- BucketSort(桶排序)原理及C++代码实现
桶排序假设输入数据服从均匀分布,平均情况下它的时间复杂度为O(n). 桶排序将输入数据的区间均匀分成若干份,每一份称作“桶”.分别对每一个桶的内容进行排序,再按桶的顺序输出则完成排序. 通常使用链表来 ...
- 文本输入框input将输入转换为统一大小写
转载地址:http://blog.csdn.net/yieryi_/article/details/52078596 文本输入框input将输入转换为统一大小写,通常有两种方法:JS和CSS方法. 1 ...
- Fourier级数
目录 Fourier级数 函数的Fourier级数的展开 Fourier级数习题: Fourier级数 函数的Fourier级数的展开 Euler--Fourier公式 我们探讨这样一个问题: 假设\ ...
- Mysql分区,分库和分表
作者说的非常清楚了,感谢.地址为:http://haitian299.github.io/2016/05/26/mysql-partitioning/. 本人项目实践,使用sharding-jdbc进 ...