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

双向链表详解

什么是双向链表?

在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日

相关文章

  • iOS中输入框设置指定字符输入的方法

    Sure! 下面是关于在iOS中设置指定字符输入的方法的完整攻略,包含两个示例说明。 方法一:使用代理方法 创建一个遵循UITextFieldDelegate协议的类,并将其设置为输入框的代理对象。 class MyTextFieldDelegate: NSObject, UITextFieldDelegate { func textField(_ text…

    other 2023年8月18日
    00
  • C++自定义数据类型方法详情

    下面为您详细讲解“C++自定义数据类型方法详情”的完整攻略。 什么是自定义数据类型? 在C++中,自定义数据类型指的是用户可以自定义的数据类型,也就是不属于C++预定义数据类型的类型。通过自定义数据类型,我们可以更加方便地封装程序所需要的数据,并且使代码可读性更强、代码复用性更好、程序稳定性更高。常见的自定义数据类型有结构体(struct)、枚举类型(enu…

    other 2023年6月27日
    00
  • djvu文件怎么打开

    关于如何打开djvu文件,我将为你提供一份完整的攻略。 什么是djvu文件 DjVu是一种图像文件格式,以其高压缩率和高质量的图像而闻名。它通常用于扫描文档、杂志和书籍等图像文档的存储和传输。 DjVu文件的扩展名为.djvu。 如何打开djvu文件 要打开djvu文件,我们需要使用相关的软件。以下是几种常见的打开djvu文件的方式。 1. 使用DjView…

    其他 2023年4月16日
    00
  • Win10如何解决重启后桌面图标重新排列

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

    other 2023年6月27日
    00
  • go基础语法50问及方法详解

    Go基础语法50问及方法详解攻略 1. 介绍 \”Go基础语法50问及方法详解\”是一本针对Go语言初学者的教程,旨在帮助他们快速入门并掌握Go语言的基础语法和常用方法。本攻略将详细讲解该教程的内容,并提供两个示例来说明相关概念。 2. 示例1:变量声明和赋值 问题:如何在Go中声明和赋值变量? 解答:在Go中,可以使用关键字var来声明变量,并使用=进行赋…

    other 2023年7月29日
    00
  • 分析Android中应用的启动流程

    分析 Android 中应用的启动流程可以分为以下几个步骤: 操作系统启动应用进程 当用户点击应用图标启动应用时,操作系统首先会启动应用进程。此时,操作系统会执行应用的启动代码,并调用 Android Framework 提供的入口函数 onCreate()。 应用进程启动主线程 应用进程启动后,会先创建主线程,然后主线程根据 AndroidManifest…

    other 2023年6月20日
    00
  • MyBatis实现两种查询树形数据的方法详解(嵌套结果集和递归查询)

    MyBatis是一种优秀的ORM(对象关系映射)框架,它可以帮助我们更加方便地进行数据库操作。MyBatis不仅可以处理常规的查询操作,还可以处理一些比较复杂的场景,比如树形结构的数据查询。而在树形结构数据查询中,常用的方法有两种:嵌套结果集和递归查询。本文将详细讲解这两种方法的实现过程。 一、嵌套结果集实现树形结构数据查询 嵌套结果集是一种比较容易理解的方…

    other 2023年6月27日
    00
  • c#截取字符串某个字符之后的字符

    当然,我很乐意为您提供有关“C#截取字符串某个字符之后的字符”的完整攻略。以下是详细的步骤和两个示例: 1 C#截取字符串某个字符之后的字符 在C#应用程序中,有时需要截取字符串某个字符之后的字符。以下是截取字符串某个字符之后的字符的方法: 1.1 使用Substring方法 您可以使用C#的Substring方法截取字符串某个字符之后的字符。以下是使用Su…

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