双向循环链表

定义

双向循环链表和它名字的表意一样,就是把双向链表的两头连接,使其成为了一个环状链表。只需要将表中最后一个节点的next指针指向头节点,头节点的prior指针指向尾节点,链表就能成环儿,如图所示:

 

需要注意的是,虽然双向循环链表成环状,但本质上还是双向链表,因此在双向循环链表中,依然能够找到头指针和头节点等。双向循环链表和双向链表相比,唯一的不同就是双向循环链表首尾相连,其他都完全一样。

注意:因为我上面已经讲了双向链表,所以这里只注重讲他们的实现差异。另因为带头节点会更好操作,所以我的代码都有头节点。

1、双向循环链表的创建

初始化时需要将头节点的next和prior都指向自己。


//1、初始化双向循环链表(带头节点)

Status initLinkList(LinkList *list){

    //创建头节点

    *list = malloc(sizeof(Node));

    if (*list == NULL) {

        return ERROR;

    }

    //前驱和后继都指向自己

    (*list)->prior = *list;

    (*list)->data = -1;

    (*list)->next = *list;

    printf("已初始化链表~\n");

    return OK;

}

2、遍历双向循环链表

注意它的尾节点的next不再是Null,而是头节点

//2、遍历双向循环链表

void printfLinkLisk(LinkList list){

    printf("遍历链表:\n");

    if (list == NULL || list->next == list) {

        printf("这是一个空链表\n");

        return;

    }

    LinkList p = list;

    //判断next是否全部正确

    printf("根据next从前往后遍历:");

    while (p->next != list) {

        printf("%d ",p->next->data);

        p = p->next;

    }

    printf("\n");

    //判断prior是否全部正确

    printf("根据prior从后往前遍历:");

    while (p != list) {

        printf("%d ",p->data);

        p = p->prior;

    }

    printf("\n");

}

3、根据索引位置添加节点

这里不需要判断尾节点的next是否为Null,因为它会指向头节点。

//3、根据索引位置插入数据至链表中

Status insertLinkList(LinkList *list, int index, ElemType data){

    if (list == NULL || index < 0) {

        return ERROR;

    }

    int i = 0;

    LinkList priorNode = *list;

    //判断插入的位置,这里开始位置是0,index超过链表长度则插入末尾

    while (i < index && priorNode->next != *list) {

        priorNode = priorNode->next;

        i++;

    }

    LinkList newNode = malloc(sizeof(Node));

    if (newNode == NULL) {

        return ERROR;

    }

    newNode->data = data;

    //插入操作共四步,看好了,别眨眼

    //1.将priorNode->next节点的前驱指向新节点

    priorNode->next->prior = newNode;

    //2.将新节点->next指向原来的priorNode->next

    newNode->next = priorNode->next;

    //3.将priorNode->next指向新节点

    priorNode->next = newNode;

    //4.新节点的前驱指向priorNode

    newNode->prior = priorNode;

    return OK;

}

4、根据索引位置删除节点

这里不需要判断尾节点的next是否为Null,因为它会指向头节点。

//4、根据索引位置删除节点

Status deleteLinkListByIndex(LinkList *list, int index, ElemType *data){

    if (*list == NULL || index < 0) {

        return ERROR;

    }

    LinkList locaNode = *list;

    int i = 0;

    //注意别删了头节点

    while (i <= index) {

        locaNode = locaNode->next;

        if (locaNode == *list) {

            printf("没有这个你想要删除的节点\n");

            return ERROR;

        }

        i++;

    }

    //开始删除,只需要做两步

    locaNode->prior->next = locaNode->next;

    locaNode->next->prior = locaNode->prior;

    *data = locaNode->data;

    free(locaNode);

    return OK;

}

5、根据存储的值删除节点

这里不需要判断尾节点的next是否为Null,因为它会指向头节点。

//5、根据存储的值删除节点

Status deleteLinkListByData(LinkList *list, ElemType data){

    if (*list == NULL) {

        return ERROR;

    }

    LinkList locaNode = (*list)->next;

    while (locaNode != *list) {

        if (locaNode->data == data) {

            break;

        }

        locaNode = locaNode->next;

    }

    if (locaNode == *list) {

        printf("没有这个你想要删除的节点\n");

        return ERROR;

    }

    //开始删除,只需要做两步

    locaNode->prior->next = locaNode->next;

    locaNode->next->prior = locaNode->prior;

    free(locaNode);

    return OK;

}

6、根据值查找节点

尾节点的next可是头节点哦,找到它就是最后一个了。

//6、查找元素

Status selectNode(LinkList list, ElemType data, LinkList *locaNode){

    if (list == NULL) {

        return ERROR;

    }

    LinkList p = list->next;

    while (p != list) {

        if (p->data == data) {

            *locaNode = p;

            break;

        }

        p = p->next;

    }

    if (*locaNode == NULL) {

        printf("没有这个你想要的节点\n");

        return ERROR;

    }

    else {

        return OK;

    }

}

其它辅助代码

#include "stdlib.h"

#define OK    1

#define ERROR 0

//元素类型

typedef int ElemType;

//状态类型

typedef int Status;

//定义节点结构体

typedef struct Node {

    struct Node *prior;

    ElemType data;

    struct Node *next;

} Node;

typedef Node *LinkList;

int main(int argc, const char * argv[]) {

    LinkList list;

    initLinkList(&list);

    for (int i = 0; i < 10; i ++) {

        insertLinkList(&list, i, i);

    }

    printfLinkLisk(list);

    int index, data;

    printf("输入你想插入的位置(从0开始)和存储的值:");

    scanf("%d %d",&index,&data);

    insertLinkList(&list, index, data);

    printfLinkLisk(list);

    printf("输入你想删除的位置(从0开始):");

    scanf("%d",&index);

    deleteLinkListByIndex(&list, index, &data);

    printfLinkLisk(list);

    printf("输入你想删除的节点的值(只删最前的那个):");

    scanf("%d",&data);

    deleteLinkListByData(&list, data);

    printfLinkLisk(list);

    printf("\n");

    return 0;

}

输出结果:

 

—END—

看到这里是不是又学到了很多新知识呢~

如果你很想学编程,小编推荐我的C语言/C++编程学习基地【点击进入】!

都是学编程小伙伴们,带你入个门还是简简单单啦,一起学习,一起加油~

还有许多学习资料和视频,相信你会喜欢的!

涉及:游戏开发、常用软件开发、编程基础知识、课程设计、黑客等等......

 

 

【C语言教程】“双向循环链表”学习总结和C语言代码实现!的更多相关文章

  1. 【C语言教程】双向链表学习总结和C语言代码实现!值得学习~

    双向链表 定义 我们一开始学习的链表中各节点中都只包含一个指针(游标),且都统一指向直接后继节点,通常称这类链表为单向链表. 虽然使用单向链表能 100% 解决逻辑关系为 "一对一" ...

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

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

  3. 数据结构8: 双向链表(双向循环链表)的建立及C语言实现

    之前接触到的链表都只有一个指针,指向直接后继,整个链表只能单方向从表头访问到表尾,这种结构的链表统称为 “单向链表”或“单链表”. 如果算法中需要频繁地找某结点的前趋结点,单链表的解决方式是遍历整个链 ...

  4. c语言实现--双向循环链表操作

    1,双向链表相当于两个单向循环链表. 2,双向链表的结点定义. 1 struct DULNode 2 { 3 int data; 4 struct DULNode * prior; 5 struct ...

  5. C语言实现双向循环链表

    #include <stdio.h> #include <stdlib.h> #include <string.h> struct list_head { stru ...

  6. Swift语言教程中文文档

    Swift语言教程中文文档 Swift语言教程(一)基础数据类型 Swift语言教程(二)基础数据类型 Swift语言教程(三)集合类型 Swift语言教程(四) 集合类型 Swift语言教程(五)控 ...

  7. Swift3.0语言教程字符串转换为数字值

    Swift3.0语言教程字符串转换为数字值 Swift3.0语言教程字符串转换为数字值,在NSString中,开发者可以将字符串转换为数字值,通过这些数字值可以实现一些功能,如加法运算.减法运算等.数 ...

  8. Swift3.0语言教程比较、判断字符串

    Swift3.0语言教程比较.判断字符串 Swift3.0语言教程比较.判断字符串,在一个程序中字符串很多时,常常会做的操作就是对这些字符串进行比较和判断.本小节将讲解这些内容. 1.不区分大小写比较 ...

  9. Xamarin XAML语言教程构建进度条ProgressBar

    Xamarin XAML语言教程构建进度条ProgressBar Xamarin XAML语言教程构建进度条ProgressBar,ProgressBar被称为进度条,它类似于没有滑块的滑块控件.进度 ...

随机推荐

  1. 采用GitOps的11大原因

    Kubernetes允许我们单纯地使用声明性的配置文件来管理我们的应用部署和其他基础设施组件(例如,我们现在都是YAML开发者).这使我们能够把所有这些文件放到Git仓库中,然后把它挂到流水线上(Je ...

  2. 第一次编程作业(My Own Score)

    博客班级 https://edu.cnblogs.com/campus/fzzcxy/2018SE2 作业要求 https://edu.cnblogs.com/campus/fzzcxy/2018SE ...

  3. SDWebImageInfo

    SDWebImage 简介 iOS中著名的牛逼的网络图片处理框架 包含的功能:图片下载.图片缓存.下载进度监听.gif处理等等 用法极其简单,功能十分强大,大大提高了网络图片的处理效率 国内超过90% ...

  4. [POJ]Find The Multiple(DFS)

    题目链接 http://poj.org/problem?id=1426 题意 输入一个数n,输出任意一个 只含0.1且能被n整除的数m.保证n<=200,m最多100位. 题解 DFS/BFS都 ...

  5. windows下nginx的配置

    这里做的nginx的配置主要的功能是: 能够用localhost访问本地文件夹中的项目 输入ip地址访问本地文件夹中的项目 反向代理其他地址访问本地文件 1.nginx安装地址 2.解压之后的文件如下 ...

  6. Shell编程(3)

    1. shell流程控制 2. for语句 3. while语句 4. break和continue语句 5. case语句 6. shell编程高级实战 shell流程控制 流程控制是改变程序运行顺 ...

  7. Linux磁盘管理及LVM讲解(1)

    硬盘接口 从整体的角度上,硬盘接口分为IDE.SATA.SCSI和SAS四种,IDE接口硬盘多用于家用产品中,也部分应用于服务器,SCSI接口的硬盘则主要应用于服务器市场,而SAS只在高端服务器上,价 ...

  8. 【小白学PyTorch】12 SENet详解及PyTorch实现

    文章来自微信公众号[机器学习炼丹术].我是炼丹兄,有什么问题都可以来找我交流,近期建立了微信交流群,也在朋友圈抽奖赠书十多本了.我的微信是cyx645016617,欢迎各位朋友. 参考目录: @ 目录 ...

  9. mysql之慢日志查询

    转自https://my.oschina.net/wuweixiang/blog/2987434 首先得配置my.cnf: #===================================== ...

  10. Map遍历法则

    /** * 如果既要遍历key又要value,那么建议这种方式,应为如果先获取keySet然后再执行map.get(key),map内部会执行两次遍历. * 一次是在获取keySet的时候,一次是在遍 ...