C语言中带头双向循环链表基本操作的实现详解

yizhihongxing

C语言中带头双向循环链表基本操作的实现详解

什么是带头双向循环链表

带头双向循环链表是一种常见的数据结构,在实际开发中也经常会用到。带头双向循环链表可以看作是一种特殊的链表,相对于普通链表,它具有以下特点:

  1. 它有一个头结点,头结点不存储数据,它的作用是指向链表中的第一个节点。

  2. 每个节点都有一个前驱指针prev和一个后继指针next,用于指向前一个节点和后一个节点。

  3. 最后一个节点的后继指针指向头结点,第一个节点的前驱指针也指向头结点,从而形成一个循环链表。

带头双向循环链表的结构如下:

typedef struct DNode {
    int data;
    struct DNode *prev;
    struct DNode *next;
} DNode, *DLinkList;

typedef struct DNodeWithHead {
    int length; //存储链表的长度
    struct DNode *header;
} DList, *DLinkedList;

基本操作及其实现

1. 初始化链表

初始化带头双向循环链表,需要为链表分配内存空间,并且将头结点的prev和next都指向自身。

DLinkedList init() {
    DLinkedList list = (DLinkedList)malloc(sizeof(DList));
    list->length = 0;
    list->header = (DNode *)malloc(sizeof(DNode));
    list->header->prev = list->header;
    list->header->next = list->header;

    return list;
}

2. 插入节点

向带头双向循环链表中插入节点,需要先判断插入的位置是否合法,然后才能进行插入操作。这里以在指定位置p后插入一个新节点为例。

int insert(DLinkedList list, int p, int data) {
    if (p < 1 || p > list->length + 1) {
        return 0; //插入位置不合法
    }

    DNode *node = (DNode *)malloc(sizeof(DNode));
    node->data = data;

    DNode *pre = list->header; //pre指向头节点
    while (p-- > 1) {
        pre = pre->next; //pre指向要插入位置的前一个节点
    }

    node->next = pre->next; //新节点的后继指向插入位置的后一个节点
    pre->next->prev = node; //插入位置的后一个节点的前驱指向新节点
    pre->next = node; //插入位置的前一个节点的后继指向新节点
    node->prev = pre; //新节点的前驱指向插入位置的前一个节点

    list->length++; //链表长度加一
    return 1;
}

3. 删除节点

在带头双向循环链表中删除节点,需要先判断删除的位置是否合法,然后才能进行删除操作。这里以删除指定位置p上的节点为例。

int deleteNode(DLinkedList list, int p) {
    if (p < 1 || p > list->length) {
        return 0; //删除位置不合法
    }

    DNode *pre = list->header; //pre指向头节点
    while (p-- > 1) {
        pre = pre->next; //pre指向要删除的节点的前一个节点
    }

    DNode *del = pre->next; //del指向要删除的节点
    pre->next = del->next; //删除节点的前驱指向删除节点的后继
    del->next->prev = pre; //删除节点的后继指向删除节点的前驱
    free(del); //释放要删除的节点的内存

    list->length--; //链表长度减一
    return 1;
}

示例

下面给出两个例子来说明带头双向循环链表的使用。

示例1:循环队列

带头双向循环链表可以用来实现循环队列。在循环队列中,队列的队尾指针需要满足环形的特性,因此我们可以借助带头双向循环链表的结构来实现循环队列。以下是循环队列的代码实现:

typedef struct {
    DLinkedList list; //带头双向循环链表
    int rear; //队尾指针
} Queue, *QueuePtr;

QueuePtr initQueue() {
    QueuePtr queue = (QueuePtr)malloc(sizeof(Queue));
    queue->list = init();
    queue->rear = 0;
    return queue;
}

int isEmpty(QueuePtr queue) {
    return queue->list->length == 0;
}

int enqueue(QueuePtr queue, int data) {
    if (insert(queue->list, queue->list->length + 1, data)) {
        queue->rear = (queue->rear + 1) % queue->list->length; //更新队尾指针
        return 1;
    }

    return 0;
}

int dequeue(QueuePtr queue) {
    if (isEmpty(queue)) {
        return 0;
    }

    int data = queue->list->header->next->data;
    deleteNode(queue->list, 1);
    queue->rear--; //更新队尾指针
    return data;
}

示例2:LRU缓存

LRU(Least Recently Used)是一种缓存淘汰算法,其原理是根据数据最后一次被访问的时间来决定何时从缓存中淘汰数据。带头双向循环链表可以用来实现LRU缓存,我们可以将链表的头部作为最近访问的数据,尾部作为最久未访问的数据。以下是LRU缓存的代码实现:

typedef struct {
    DLinkedList list; //带头双向循环链表
    int capacity; //缓存大小
} LRUCache, *LRUCachePtr;

LRUCachePtr initCache(int capacity) {
    LRUCachePtr cache = (LRUCachePtr)malloc(sizeof(LRUCache));
    cache->list = init();
    cache->capacity = capacity;
    return cache;
}

int get(LRUCachePtr cache, int key) {
    DNode *node = cache->list->header->next;
    while (node != cache->list->header) {
        if (node->data == key) {
            deleteNode(cache->list, getPos(cache->list, key));
            insert(cache->list, 1, key); //将访问的数据移动到链表头部
            return key;
        }

        node = node->next;
    }

    return -1; //如果缓存中不存在该数据,则返回-1
}

int put(LRUCachePtr cache, int key) {
    int pos = getPos(cache->list, key);
    if (pos > 0) { //如果缓存中已经存在该数据,则将其移动到链表头部
        deleteNode(cache->list, pos);
        insert(cache->list, 1, key);
        return key;
    }

    if (cache->list->length >= cache->capacity) { //如果缓存已满,则淘汰最久未被访问的数据
        deleteNode(cache->list, cache->list->length);
    }

    insert(cache->list, 1, key);
    return key;
}

总结

带头双向循环链表是一种常用的数据结构,在实际开发中也经常会用到。本文详细讲解了带头双向循环链表的基本操作及其实现,并通过两个示例简单介绍了带头双向循环链表的应用。希望本文能够对大家理解带头双向循环链表的使用和原理有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言中带头双向循环链表基本操作的实现详解 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • autouninstaller密钥

    以下是“autouninstaller密钥”的完整攻略: autouninstaller密钥 autouninstaller是一个用于自动卸载软件的工具,它可以帮助您快速干净地卸载软件。autouninstaller密钥是一个用于激活autouninstaller的密钥。本攻略将介绍如何获取和使用autouninstaller密钥。 步骤1:购买autoun…

    other 2023年5月7日
    00
  • Ubuntu有望正式支持ZFS文件系统

    Ubuntu有望正式支持ZFS文件系统,这将使得存储管理变得更加易于管理和操控。下面详细讲解Ubuntu官方支持ZFS文件系统的完整攻略: 安装 ZFS 首先,我们需要安装ZFS文件系统。在Ubuntu中可以通过下面的命令来安装ZFS。 sudo apt-get install zfsutils-linux 创建并挂载ZFS文件系统 安装好ZFS之后,我们就…

    other 2023年6月27日
    00
  • js实现千位分隔

    js实现千位分隔 在前端开发中,我们常会遇到需要对数值进行千位分隔的情况,即将数值以3位一组的形式进行分隔,并用逗号将其连接起来展示在页面上,以提高数字的可读性。下面,我们来介绍一下如何使用Js实现千位分隔。 方法一:正则表达式 正则表达式是一种强大的文本处理工具,可以用来进行字符串的匹配和替换,也可以用来实现千位分隔。实现方式如下: function fo…

    其他 2023年3月28日
    00
  • 基于Vue制作组织架构树组件

    什么是组织架构树组件?组织架构树组件是一种常见的前端组件,用于显示企业或组织机构的人员层级关系,可以让用户清晰地了解整个组织的人员关系、职位层级等信息。 Vue是什么?Vue是一款轻量级的JavaScript框架,被广泛用于开发Web应用程序。Vue具有极高的灵活性和可定制性,允许开发人员轻松构建复杂的Web组件并实现数据双向绑定和响应式UI设计。 制作组织…

    other 2023年6月27日
    00
  • c-为什么%d代表整数?

    在C语言中,%d是用于格式化输出整数的占位符。在C语言中,整数是一种基本数据类型,用于表示整数值。本文将详细讲解为什么%d代表整数,并提供两个示例说明。 为什么%d代表整数? 在C语言中,%d是用于格式化输出整数的占位符。这是因为在C语言中,整数是一种基本数据类型,用于表示整数值。在使用printf函数输出整数时,需要使用%d占位符来指定输出整数的格式。 %…

    other 2023年5月7日
    00
  • 谈一谈基于python的面向对象编程基础

    基于Python的面向对象编程基础 面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式,它将数据和操作数据的方法组织在一起,形成对象。Python是一种支持面向对象编程的高级编程语言,提供了丰富的语法和特性来支持面向对象编程。 类和对象 在Python中,类是创建对象的蓝图或模板,对象是类的实例。类定义了对象的属…

    other 2023年10月15日
    00
  • IP地址与整数之间的转换实现代码(asp.net)

    当将IP地址与整数之间进行转换时,可以使用以下代码实现: using System; using System.Net; public class IPAddressConverter { public static long IPToLong(string ipAddress) { IPAddress ip = IPAddress.Parse(ipAddr…

    other 2023年7月30日
    00
  • Centos纯命令行文本界面下如何安装桌面?

    下面是详细的攻略步骤: 1. 确认系统版本 在CentOS终端输入以下命令查看CentOS版本: cat /etc/redhat-release 2. 安装桌面环境 在CentOS终端输入以下命令进行桌面环境的安装: yum groupinstall "X Window System" "GNOME Desktop" …

    other 2023年6月26日
    00
合作推广
合作推广
分享本页
返回顶部