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内存数据库软件,同时也是一款卓越的数据结构服务软件.它支持字符串.列表.哈希表.集合.有序集合五种数据结构类型,同时每种数据结构类型针对不同的应用场景又支 ...
随机推荐
- 903A. Hungry Student Problem#饥饿的学生(暴力&双层枚举)
题目出处:http://codeforces.com/problemset/problem/903/A 题目大意就是:一个数能否用正整数个另外两个数合成 #include<iostream> ...
- 新年在家学java之基础篇-参数&修饰符&构造器
可变参数 不知道可能给方法传递多少个参数时使用这个方法 public void printInfo (String[] args) --可以定义一个数组,在调用这个方法适合赋值给一个数组 public ...
- java通过免费接口获取ip地址的服务商信息
今天分享一个免费在线的小工具的开发代码就是通过淘宝提供的接口获取服务商信息,工具地址:http://www.yzcopen.com/seo/ipadress 代码如下: public class Yz ...
- Codeforces 558E A Simple Task(计数排序+线段树优化)
http://codeforces.com/problemset/problem/558/E Examples input 1 abacdabcda output 1 cbcaaaabdd input ...
- eclipse web 项目配置 运行
- 关于vyos 防火墙配置
VyOS是一个基于Debian的网络操作系统,是Vyatta的社区fork.Vyatta是博通的企业级的产品,通过这套系统,能在x86平台提供路由,防火墙和×××的功能. 这个系统提供了和其他诸如Ci ...
- 虚拟环境(virtualenv)
为什么需要虚拟环境: 到目前位置,我们所有的第三方包安装都是直接通过pip install xx的方式进行安装的,这样安装会将那个包安装到你的系统级的Python环境中.但是这样有一个问题,就是如果你 ...
- 简单标签 SimpleTagSupport示例
最近处理JSP页面,需要把数据库查到的原始值,根据数据字典转换成更加直观的值.比如查到的结果是 01,jsp页面展示‘身份证’. 如果值比较少,就直接用c:if标签处理了,无奈接触的值比较多,只想到了 ...
- [LC] 139. Word Break
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine ...
- vue element 关闭当前tab 跳转到上一路由
方法一 this.$store.dispatch('delVisitedViews', this.$route); this.$router.go(-1); 方法二 this.$store.state ...