C语言中双向链表和双向循环链表详解

yizhihongxing

双向链表详解

什么是双向链表?

在C语言中,双向链表是一种常用的数据结构,它是由一系列节点组成,每个节点有两个指针,一个指向前一个节点,一个指向后一个节点。

双向链表与单向链表相比,多了指向前一个节点的指针,这使得我们可以很方便地实现双向遍历,提高了搜索效率。

双向链表中节点的定义

struct Node {
    int data;
    struct Node *prev;  // 指向前一个节点
    struct Node *next;  // 指向后一个节点
};

节点需要包含数据,以及两个指向前后节点的指针。

双向链表的创建

双向链表的创建可以通过循环遍历链表来实现,依次创建节点,让前一个节点的next指针指向当前节点,当前节点的prev指针指向前一个节点。

struct Node *create(int array[], int size) {
    struct Node *head = NULL;
    struct Node *prev = NULL;
    for (int i = 0; i < size; ++i) {
        struct Node *node = (struct Node *)malloc(sizeof(struct Node));
        node->data = array[i];
        node->prev = prev;
        node->next = NULL;
        if (prev) {
            prev->next = node;
        } else {
            head = node;
        }
        prev = node;
    }
    return head;
}

双向链表的插入

双向链表的插入操作可以分为头部插入和尾部插入,分别使用head和tail指针来进行操作。

头部插入

头部插入时,我们需要将新节点的next指向当前头节点,将当前头节点的prev指向新节点,然后再将head指针指向新节点。

void insert_head(struct Node **head, int data) {
    struct Node *node = (struct Node *)malloc(sizeof(struct Node));
    node->data = data;
    node->prev = NULL;
    node->next = *head;
    if (*head) {
        (*head)->prev = node;
    }
    *head = node;
}

尾部插入

尾部插入时,我们需要先遍历到链表的末尾节点,再将新节点插入到末尾节点后面。需要先判断链表是否为空,为空时直接创建新节点作为头节点。

void insert_tail(struct Node **head, int data) {
    struct Node *node = (struct Node *)malloc(sizeof(struct Node));
    node->data = data;
    node->prev = NULL;
    node->next = NULL;
    if (*head == NULL) {
        *head = node;
    } else {
        struct Node *tail = *head;
        while (tail->next != NULL) {
            tail = tail->next;
        }
        tail->next = node;
        node->prev = tail;
    }
}

双向链表的删除

双向链表的删除操作需要注意维护prev和next指针的指向,我们需要先定位到要删除的节点,将它的prev节点的next指针指向它的next节点,将它的next节点的prev指针指向它的prev节点,然后再释放该节点的内存。

void delete(struct Node **head, int key) {
    struct Node *node = *head;
    while (node != NULL) {
        if (node->data == key) {
            if (node->prev) {
                node->prev->next = node->next;
            } else {
                *head = node->next;
            }
            if (node->next) {
                node->next->prev = node->prev;
            }
            free(node);
            return;
        }
        node = node->next;
    }
}

示例1:双向链表的遍历

双向链表的遍历可以从头节点开始,按照next指针依次向后遍历,输出每个节点的数据。

void traverse(struct Node *head) {
    printf("双向链表的数据:");
    struct Node *node = head;
    while (node != NULL) {
        printf("%d ", node->data);
        node = node->next;
    }
    printf("\n");
}

示例2:双向链表的逆序遍历

双向链表可以很方便地逆序遍历,只需要从尾节点开始,按照prev指针依次向前遍历,输出每个节点的数据。

void reverse_traverse(struct Node *head) {
    printf("双向链表逆序遍历的数据:");
    struct Node *node = head;
    while (node != NULL && node->next != NULL) {
        node = node->next;
    }
    while (node != NULL) {
        printf("%d ", node->data);
        node = node->prev;
    }
    printf("\n");
}

双向循环链表详解

双向循环链表是在双向链表的基础上,将头节点和尾节点相连接形成一个环,可以实现头尾节点互相指向,提高了链表的搜索效率和代码的简洁度。

双向循环链表中节点的定义

节点需要包含数据,以及两个指向前后节点的指针。与双向链表不同的是,尾节点的next指针需要指向头节点,头节点的prev指针需要指向尾节点。

struct Node {
    int data;
    struct Node *prev;  // 指向前一个节点
    struct Node *next;  // 指向后一个节点
};

双向循环链表的创建

双向循环链表的创建与双向链表类似,只需要在最后一个节点的next指针指向头节点,并让头节点的prev指向尾节点即可。

struct Node *create(int array[], int size) {
    struct Node *head = NULL;
    struct Node *prev = NULL;
    for (int i = 0; i < size; ++i) {
        struct Node *node = (struct Node *)malloc(sizeof(struct Node));
        node->data = array[i];
        node->prev = prev;
        node->next = NULL;
        if (prev) {
            prev->next = node;
        } else {
            head = node;
        }
        prev = node;
    }
    if (prev) {
        prev->next = head;
    }
    if (head) {
        head->prev = prev;
    }
    return head;
}

示例1:双向循环链表的遍历

双向循环链表的遍历可以从头节点开始,按照next指针依次向后遍历,输出每个节点的数据。需要注意终止条件,当遍历到头节点时,完成一次遍历。

void traverse(struct Node *head) {
    printf("双向循环链表的数据:");
    struct Node *node = head;
    do {
        printf("%d ", node->data);
        node = node->next;
    } while (node != head);
    printf("\n");
}

示例2:双向循环链表的逆序遍历

双向循环链表可以很方便地逆序遍历,只需要从尾节点开始,按照prev指针依次向前遍历,输出每个节点的数据。需要注意终止条件,当遍历到尾节点时,完成一次遍历。

void reverse_traverse(struct Node *head) {
    printf("双向循环链表逆序遍历的数据:");
    struct Node *node = head;
    while (node != NULL && node->next != head) {
        node = node->next;
    }
    do {
        printf("%d ", node->data);
        node = node->prev;
    } while (node != NULL && node->next != head);
    printf("\n");
}

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

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

相关文章

  • DB2获取当前用户表、字段、索引等详细信息

    获取当前用户表、字段、索引等详细信息是DB2数据库管理中一个常见的操作需求,可以通过DB2系统表进行查询。下面是完整的攻略: 1.查询当前用户下所有表 可以通过查询SYSCAT.TABLES系统表获取当前用户下的所有表信息,包括表名、表所属的模式名、表所属的空间名以及表的类型等。查询语句如下: SELECT TABNAME, TABSCHEMA, TBCRE…

    other 2023年6月25日
    00
  • Win10共享登录帐户名怎么设置显示或隐藏?

    Win10共享登录帐户名是指多个用户可以共享同一个帐户登录电脑,此时,登录界面将显示该共享帐户的用户名,但是,有些用户由于安全等方面的考虑,希望隐藏该共享帐户的用户名。那么,如何在Win10中设置共享帐户的用户名的显示或隐藏呢?下面是详细攻略: 第一步:进入注册表编辑器 Win10共享登录帐户名的设置需要通过注册表编辑器实现,按下 Win+R 快捷键,同时在…

    other 2023年6月27日
    00
  • powerbi度量值分组统计

    Power BI度量值分组统计 概述 在使用Power BI处理数据时,度量值的分组统计是必不可少的操作之一。本文将介绍如何通过Power BI对度量值进行分组统计,使得数据更加直观、易于分析和理解。 步骤 步骤一:建立数据模型 在Power BI中导入数据源,并创建数据模型。假设我们要对销售额进行分组统计,数据源包含了以下几个字段:销售日期、销售额、商品名…

    其他 2023年3月28日
    00
  • vue报表开发

    Vue报表开发 随着互联网的发展,数据分析和数据可视化变得愈发重要,作为前端开发者,我们需要快速、高效地开发出精美的报表界面来满足用户需求。Vue作为一款优秀的前端框架,具有极高的灵活性和扩展性,这使得它成为开发报表的最佳选择。 Vue报表框架推荐 市面上出现了很多优秀的Vue报表框架,例如: ECharts AntV G2 BizCharts 以上三种报表…

    其他 2023年3月29日
    00
  • 复杂系统中的用户权限数据库设计解决方案

    我来为你讲解“复杂系统中的用户权限数据库设计解决方案”的完整攻略。 一、设计需求分析 1.1 系统架构简述 首先我们需要了解复杂系统的架构,从而确定我们需要设计的用户权限数据库解决方案。复杂系统通常由多个子系统组成,这些子系统之间存在着不同的数据访问权限和使用权限。 在这样的系统架构下,我们需要设计一个用户权限数据库,用于存储用户与资源之间的关系,并根据用户…

    other 2023年6月26日
    00
  • Thinkphp框架使用list_to_tree 实现无限级分类列出所有节点示例

    首先,我们需要了解什么是list_to_tree。这是一个 Thinkphp 框架提供的函数,用于将一个平面的数组转换成树形结构,也就是将数组中的每一个节点,根据其在数组中的位置关系,转换成一颗多级的树状结构。在无限级分类方面,它经常用于将分类节点表中的数据从平面的列表格式,转换成具有层级关系的树形结构。 下面是 Thinkphp 框架使用 list_to_…

    other 2023年6月27日
    00
  • Java中继承、多态、重载和重写介绍

    我来讲解一下。 继承 继承是Java面向对象编程中的一个重要的特性。它允许我们创建一个新的类,以现有类的特性为基础,从而减少了代码的重复编写。下面是一个简单的继承示例: public class Animal { public void move() { System.out.println("动物可以移动"); } } public c…

    other 2023年6月27日
    00
  • Win10如何解决重启后桌面图标重新排列

    Win10如何解决重启后桌面图标重新排列的问题,以下是完整攻略: 1. 禁用“自动排列图标”功能 Win10系统默认启用“自动排列图标”功能,这会导致桌面图标在重启后重新排列。因此,禁用此功能就可以解决问题。 具体步骤如下: 右键单击桌面空白处,选择“查看”。 将“自动排列图标”选项取消勾选。 这样,在重启后,桌面上的图标就不会重新排列了。 2. 使用桌面图…

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