【数据结构】双链表的定义和操作

2023-12-17

目录

1.双链表的定义

2.双链表的创建和初始化

3.双链表的插入节点操作

4.双链表的删除节点操作

5.双链表的查找节点操作

6.双链表的更新节点操作

7.完整代码


????嗨!我是 Filotimo__ ????。很高兴与大家相识,希望我的博客能对你有所帮助。

????本文由 Filotimo__ ✍️原创,首发于CSDN????。

????如需转载,请事先与我联系以获得授权⚠️。

????欢迎大家给我点赞????、收藏⭐️,并在留言区????与我互动,这些都是我前进的动力!

????我的格言:森林草木都有自己认为对的角度????。

1.双链表的定义

双链表是一种常见的数据结构,它由一系列节点组成,每个节点包含两个指针,一个指向前一个节点,另一个指向后一个节点。与单链表不同的是,双链表的节点可以双向访问,因此可以在任意位置快速插入、删除和查找元素。

一个双链表通常包含以下属性和操作:
头节点(head):指向第一个节点的指针。
尾节点(tail):指向最后一个节点的指针。
节点(node):包含数据和两个指针的数据单元。
插入(insert):在指定位置插入一个新的节点。
删除(delete):删除指定位置的节点。
查找(search):根据给定的值查找节点。
遍历(traverse):按顺序访问链表中的每个节点。

2.双链表的创建和初始化

创建一个双链表需要定义一个结构体,包含数据域和前后指针域。初始化时要注意将头结点的前后指针均指向 NULL。

struct DNode {
    int data;  //数据域
    struct DNode *prior;  //前驱指针域
    struct DNode *next;   //后继指针域
};

struct DNode *createList() {
    struct DNode *head = (struct DNode*)malloc(sizeof(struct DNode));
    head->prior = NULL;
    head->next = NULL;
    return head;
}

定义结构体struct DNode表示双向链表的节点。
该节点包括三个成员变量:
int data表示节点的数据域,可以存储整数类型的数据。
struct DNode *prior表示指向前一个节点的指针域。
struct DNode *next表示指向后一个节点的指针域。

在createList函数内部,首先通过malloc函数动态分配了一块内存,大小为一个struct DNode结构体的大小。然后将分配的内存强制转换为struct DNode*类型,并将其赋值给head指针变量,作为链表的头节点。

3.双链表的插入节点操作

在插入节点时,需要将新节点插入到某个节点之前或之后,通过修改前后指针实现。具体操作分为两步,先将新节点的前后指针赋值,然后修改它前后节点的指针。需要注意判断特殊情况,如插入到空链表、插入到头结点等。

void insertNode(struct DNode *p, int data) {
    if (p == NULL) return;
    struct DNode *newNode = (struct DNode*)malloc(sizeof(struct DNode));
    newNode->data = data;
    newNode->prior = p;
    newNode->next = p->next;
    if (p->next != NULL) p->next->prior = newNode;
    p->next = newNode;
}

函数insertNode接受两个参数:指向双向链表结点的指针p和要插入的数据data。

如果p不为空,则创建一个新的双向链表结点newNode,通过malloc函数动态分配内存。然后,将新节点的data字段设置为传入的data值。再将新节点的prior字段设置为指向p,将新节点的next字段设置为指向p->next。

检查p的下一个结点是否为空,如果不为空,则将下一个结点的prior字段指向新节点newNode。最后将p的next指针指向新结点newNode,完成插入操作。这样,插入操作就将新节点插入到了链表中p结点之后的位置。

4.双链表的删除节点操作

在删除节点时,需先找到要删除的节点,然后修改前后节点的指针。在执行 free 操作后,需要将指针置为 NULL,避免出现野指针。

void deleteNode(struct DNode *p) {
    if (p == NULL) return;
    p->prior->next = p->next;
    if (p->next != NULL) p->next->prior = p->prior;
    free(p);
    p = NULL;
}

如果p不为空,将p结点的前驱结点的next指针指向p结点的后继结点,断开p结点与前后结点的连接。即p->prior->next = p->next。

检查p结点的后继结点是否为空,如果不为空,则将后继结点的前驱指针指向p结点的前驱结点,断开p结点与后继结点的连接。即p->next->prior = p->prior。

使用free函数释放了指针p指向的内存,将p结点从链表中删除。

将指针p设置为NULL,确保不再引用已经删除的结点。

5.双链表的查找节点操作

查找节点时,可以采用遍历的方法进行查找。需要注意判断特殊情况,如链表为空等。

struct DNode *findNode(struct DNode *head, int data) {
    if (head == NULL) return NULL;
    struct DNode *p = head->next;
    while (p != NULL) {
        if (p->data == data) return p;
        p = p->next;
    }
    return NULL;
}

如果head不为空,代码将p指针初始化为head结点的下一个结点,即head->next。

使用while循环遍历链表,如果指针p指向的结点的data字段等于要查找的数据data,则直接返回该结点的指针p,表示查找成功。

如果没有查找到要找的数据,即p到达链表结尾指针为NULL,则返回NULL,表示查找失败。

6.双链表的更新节点操作

更新节点操作与单链表类似,具体步骤为先查找要更新的节点,然后修改其数据域的值即可。

void updateNode(struct DNode *p, int newData) {
    if (p != NULL) {
        p->data = newData;
    }
}

首先检查指针p是否为空,如果不为空,则将指针p所指向的结点的data字段更新为新的数据newData。如果结点p为空,则不进行任何操作。

7.完整代码

#include <stdio.h>
#include <stdlib.h>

//双链表结构体定义
struct DNode {
    int data;  //数据域
    struct DNode *prior;  //前驱指针域
    struct DNode *next;   //后继指针域
};

//创建双链表
struct DNode *createList() {
    struct DNode *head = (struct DNode*)malloc(sizeof(struct DNode));
    head->prior = NULL;
    head->next = NULL;
    return head;
}

//插入节点
void insertNode(struct DNode *p, int data) {
    if (p == NULL) return;
    struct DNode *newNode = (struct DNode*)malloc(sizeof(struct DNode));
    newNode->data = data;
    newNode->prior = p;
    newNode->next = p->next;
    if (p->next != NULL) p->next->prior = newNode;
    p->next = newNode;
}

//删除节点
void deleteNode(struct DNode *p) {
    if (p == NULL) return;
    p->prior->next = p->next;
    if (p->next != NULL) p->next->prior = p->prior;
    free(p);
    p = NULL;
}

//查找节点
struct DNode *findNode(struct DNode *head, int data) {
    if (head == NULL) return NULL;
    struct DNode *p = head->next;
    while (p != NULL) {
        if (p->data == data) return p;
        p = p->next;
    }
    return NULL;
}

//更新节点
void updateNode(struct DNode *p, int newData) {
    if (p != NULL) {
        p->data = newData;
    }
}

//遍历双链表
void traverseList(struct DNode *head) {
    if (head == NULL) return;
    struct DNode *p = head->next;
    while (p != NULL) {
        printf("%d ", p->data);
        p = p->next;
    }
    printf("\n");
}

//测试双链表
int main() {
    struct DNode *head = createList();  //创建链表

    //插入节点
    insertNode(head, 1);
    insertNode(head, 2);
    insertNode(head, 3);
    insertNode(head, 4);

    traverseList(head);  //遍历链表

    struct DNode *p = findNode(head, 2);  //查找节点
    if (p != NULL) {
        printf("Found node: %d\n", p->data);
        updateNode(p, 5);  //更新节点
        printf("After updated, the list is:\n");
        traverseList(head);  //遍历链表
    }

    deleteNode(findNode(head, 3)); //删除节点
    printf("After deleted, the list is:\n");
    traverseList(head);  //遍历链表

    return 0;
}

输出结果如下:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【数据结构】双链表的定义和操作 的相关文章

  • C++:创建一个由用户输入大小的数组

    我想知道我们是否可以创建一个具有用户指定大小的数组 Ex int a cout lt lt Enter desired size of the array cin gt gt a int array a 上面的程序将不起作用 因为数组大小必
  • WritePrivateProfileString 未在末尾添加属性

    我正在使用以下命令在 ini 文件中写入一些属性WritePrivateProfileString函数并且一切正常 但是当我添加多行文本时 出现了问题 这是代码和输出 WritePrivateProfileString T General
  • 如何在样式中访问控件父级的属性

    我的列表视图将项目数据模板化为标签 我正在为该标签设计一种样式 但我不知道如何访问父级的 ListViewItem IsSelected 属性 编辑 尝试了下面的建议 但仍然出现异常 这是我的完整代码
  • 如何从头开始重复C程序并清理屏幕和第一个输入值?

    我是编程新手 我写了一个简单的程序 我想一次又一次地重复该程序 并且只有当用户想要退出时它才能退出 这是我的程序 include
  • for 和 while 循环中没有循环条件

    while cond fine for cond fine 但是当我删除条件部分时 while syntax compilation error for Infinite loop 这些循环内部是如何实现的 或者 编译器 解析器 如何知道中
  • 我是否必须使用我的数据库训练 Viola-Jones 算法才能获得准确的结果?

    我尝试提取面部数据库的面部特征 但我认识到 Viola Jones 算法在两种情况下效果不佳 当我尝试单独检测眼睛时 当我尝试检测嘴巴时 运作不佳 检测图像的不同部分 例如眼睛或嘴巴 或者有时会检测到其中几个 这是不可能的情况 我使用的图像
  • 如何在 ASP.NET 5/vNext/Core 中使用 Elmah?

    我对如何在 ASP NET 5 MVC 6 项目中使用 Elmah 有点困惑 我从 nuget 得到了包 它添加了 Elmah Mvc 2 1 2 到project json 中的依赖项 我不知道从这里到哪里去 以前 nuget 会向 we
  • DPI 图形屏幕分辨率像素 WinForm PrintPageEventArgs

    对于运行我的应用程序的任何显示器 Dpi 点与像素有何关系 int points Screen primary public Form1 InitializeComponent points 1 primary null void OnPa
  • Mono 和 WebRequest 速度 - 测试

    在 mono 4 6 2 linux 中 我注意到 wget 下载文件的速度与webclient DownloadString 所以我做了一个小测试来调查 为什么 wget 明显比 C 快 根据我自己的实验 使用 wget 下载 手动读取文
  • 如何获得字符串的所有字谜

    我试图找到一个字符串的所有可能的字谜并仅使用递归将它们存储在数组中 我被困住了 这就是我所拥有的一切 int main const int MAX 10 string a ABCD string arr 10 permute arr a 0
  • 在大型数据绑定 ObservableCollection 中添加/删除许多项目,而无需冻结 GUI

    我和我的团队正在开发一个 WPF 应用程序 该应用程序显示多个并发 XamDataChart 控件 由 Infragistics 提供 每个图表都绑定到不同的 ObservableCollection 最多可包含 200 万个点 对于每个图
  • 如何在 Datagridview 中为图像列提供超链接

    如何在winforms中超链接到DataGridViewImageColumn OP 评论中的代码示例 DataGridView dgv new DataGridView dgv Name dgv i dgv DataSource dsMa
  • 在 C 或 C++ 中使用逗号作为宏名称

    我想做这样的事情 define define MAX 10 000 000 undef 有什么技巧可以做到吗 编辑 我知道 C 14 中的数字分隔符 我正在寻找一种技巧来对不兼容的编译器执行相同的操作 EDIT2 请考虑Variadic M
  • 模板是如何实例化的?

    这是一个练习 来自C 入门第五版 练习 16 27 对于每个带标签的语句 解释什么 如果有 实例化发生 如果实例化了模板 请解释原因 如果 不 请解释为什么不 第677页 template
  • 在C中更改函数内的数组

    我正在学习 C 并且很困惑为什么在 main 中创建的数组不会在函数内部更改 我假设传递的数组是一个指针 并且更改指针应该更改数组 对吧 有人可以解释这种情况下发生了什么吗 谢谢你的帮助 int main int i length 10 i
  • 在源代码和预编译二进制文件之间切换

    我们的应用程序中有大量的库 库是用 C 或 C 编写的 平台 net Framework Windows 64 位 将所有内容编译为源代码需要花费大量时间 我们正在考虑切换到预构建的二进制文件 但我们仍然希望保留返回源代码的可能性 作为版本
  • 如何在 MVC 5 中设置自定义 ClaimsPrincipal?

    我创建了一个自定义主体类 public class FacebookPrincipal ClaimsPrincipal public JObject Data get set 我想用它 当用户登录时 我尝试设置 var fbP new Fa
  • scanf() 不等待用户输入[重复]

    这个问题在这里已经有答案了 我正在使用 c 中的双向链表来制作树 我在该函数中使用递归调用 但不知何故它不起作用 我的代码是 struct node int data struct node right struct node left s
  • 计算 .NET Core 项目的代码指标?

    我正在研究 ASP NET Core 和 NET Core 项目 对于经典的 C 项目 Visual Studio 2015 具有计算代码指标的功能 对于 NET Core 预览版 2 工具中缺少支持 在工具更加完整之前 有人知道解决方法吗
  • win32 内容已更改,但除非移动窗口,否则不会显示更新

    我的 win32 GUI 内容每秒都会更改 但除非手动移动窗口 否则不会显示更新 我尝试每秒弹出一个消息框来触发窗口刷新 它成功了 因此 这证明我的内容确实发生了变化 但窗口没有更新 我希望刷新窗口而不是每次都弹出消息框 有没有这样的窗口功

随机推荐