双向链表

双向表示每个节点知道自己的直接前驱和直接后继,每个节点需要三个域

查找方向可以是从左往右也可以是从右往左,但是要实现从右往左还需要终端节点的地址,所以通常会设计成双向的循环链表;

双向的循环链表

循环链表指得是终端节点的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实现原理 - 双向循环链表的更多相关文章

  1. (C语言版)链表(四)——实现双向循环链表创建、插入、删除、释放内存等简单操作

    双向循环链表是基于双向链表的基础上实现的,和双向链表的操作差不多,唯一的区别就是它是个循环的链表,通过每个节点的两个指针把它们扣在一起组成一个环状.所以呢,每个节点都有前驱节点和后继节点(包括头节点和 ...

  2. 一种神奇的双向循环链表C语言实现

    最近在看ucore操作系统的实验指导.里面提要一个双向循环链表的数据结构,挺有意思的. 其实这个数据结构本身并不复杂.在普通链表的基础上加一个前向指针,我们就得到了双向链表,再把头尾节点连起来就是双向 ...

  3. 一、Redis基本操作——String(原理篇)

    小喵的唠叨话:最近京东图书大减价,小喵手痒了就买了本<Redis设计与实现>[1]来看看.这里权当小喵看书的笔记啦.这一系列的模式,主要是先介绍Redis的实现原理(可能很大一部分会直接照 ...

  4. 双向链表、双向循环链表的JS实现

    关于链表简介.单链表.单向循环链表.JS中的使用以及扩充方法:  单链表.循环链表的JS实现 关于四种链表的完整封装: https://github.com/zhuwq585/Data-Structu ...

  5. C语言通用双向循环链表操作函数集

    说明 相比Linux内核链表宿主结构可有多个链表结构的优点,本函数集侧重封装性和易用性,而灵活性和效率有所降低.     可基于该函数集方便地构造栈或队列集.     本函数集暂未考虑并发保护. 一  ...

  6. 双向循环链表的Java版本实现

    1.单项循环列表 单向循环链表是单链表的另一种形式,其结构特点是链表中最后一个结点的指针不再是结束标记,而是指向整个链表的第一个结点,从而使单链表形成一个环.和单链表相比,循环单链表的长处是从链尾到链 ...

  7. c语言编程之双向循环链表

    双向循环链表就是形成两个环,注意每个环的首尾相连基本就可以了. 程序中采用尾插法进行添加节点. #include<stdio.h> #include<stdlib.h> #de ...

  8. Linux内核中的通用双向循环链表

    开发中接触Linux越来越多,休息放松之余,免不了翻看翻看神秘的Linux的内核.看到双向链表时,觉得挺有意思的,此文记下. 作为众多基础数据结构中的一员,双向循环链表在各种“教科书”中的实现是相当的 ...

  9. Redis压缩列表原理与应用分析

    摘要 Redis是一款著名的key-value内存数据库软件,同时也是一款卓越的数据结构服务软件.它支持字符串.列表.哈希表.集合.有序集合五种数据结构类型,同时每种数据结构类型针对不同的应用场景又支 ...

随机推荐

  1. Mac环境下pyCharm的安装,激活及使用

    1.pyCharm的安装 安装地址: https://www.jetbrains.com/pycharm/download/#section=mac 2.激活 获取注册码地址: http://idea ...

  2. 正则表达式中 /s 可以帮助“.”匹配所有的字符,包括换行,从而实现【\d\D】的功能

    正则表达式中 /s 可以帮助“.”匹配所有的字符,包括换行,从而实现[\d\D]的功能

  3. 108)PHP分页显示

    一个代码页的链接:https://www.cnblogs.com/mmykdbc/p/6688460.html 首先一个简单的代码展示: 目录关系: 数据库表格展示: 结果展示:   然后  代码展示 ...

  4. Java源码之ArrayList

    本文源码均来自Java 8 总体介绍 Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类.Set和List两个类继承于它.Set中不能包含重复的元素,也没有顺序来存放. ...

  5. EXAM-2018-8-10

    EXAM-2018-8-10 F 突然卡了一会的水题 M 这题有点坑 考虑到一个数列的第一个数肯定会有 我们可以贪心的认为最优的方案是一个数列的第一个与另一个数列所有数的和.但是很容易找到反例 1 2 ...

  6. [LC] 92. Reverse Linked List II

    Reverse a linked list from position m to n. Do it in one-pass. Note: 1 ≤ m ≤ n ≤ length of list. Exa ...

  7. JAVA中String类以及常量池和常用方法

    一.String类的定义 String类特点:String 代表字符串.java程序中所有的字符串文字(例如:"abc")都被实现为String类的子类 String类特点:长度不 ...

  8. SHELL用法九(awk练习)

    1.SHELL编程Awk语句案例实战 Awk主要是用于对文本文件进行处理,通常是逐行处理,其语法参数格式为, AWK常用参数.变量.函数详解如下: awk 'pattern + {action}' f ...

  9. php 正则获取html任意标签

    <?php $temp = ' <div class="num">1</div> <div class="num">2 ...

  10. SpringSecurity 如何提示错误

    1.可以通过authentication-failure-url="/login.html?error=1" 前端接收参数,根据参数提示 错误 2.前端vue this.myNam ...