【数据结构】线性表(四)双向链表的各种操作(插入、删除、查找、修改、遍历打印)

目录

线性表的定义及其基本操作(顺序表插入、删除、查找、修改)

四、线性表的链接存储结构

1. 单链表

2. 循环链表

3. 双向链表

a. 双向链表节点结构

b. 创建一个新的节点

c. 在链表末尾插入节点

d. 在指定位置插入节点

e. 删除指定位置的节点

f. 遍历并打印链表

g. 主函数

h. 代码整合

五、复杂性分析

1. 空间效率比较

2. 时间效率比较



 

线性表的定义及其基本操作(顺序表插入、删除、查找、修改)

        一个线性表是由零个或多个具有相同类型的结点组成的有序集合。

         按照线性表结点间的逻辑顺序依次将它们存储于一组地址连续的存储单元中的存储方式被称为线性表的顺序存储方式。按顺序存储方式存储的线性表具有顺序存储结构,一般称之为顺序表。换言之,在程序中采用定长的一维数组,按照顺序存储方式存储的线性表,被称为顺序表。

【数据结构】线性表(一)线性表的定义及其基本操作(顺序表插入、删除、查找、修改)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_63834988/article/details/132089038?spm=1001.2014.3001.5501

四、线性表的链接存储结构

1. 单链表

        顺序表的优点是存取速度快。但是,无论是插入一个结点,还是删除一个结点,都需要调整一批结点的地址。要克服该缺点,就必须给出一种不同于顺序存储的存储方式。用链接存储方式存储的线性表被称为链表,可以克服上述缺点。

        链表中的结点用存储单元(若干个连续字节)来存放,存储单元之间既可以是(存储空间上)连续的,也可以是不连续的,甚至可以零散地分布在存储空间中的任何位置。换言之,链表中结点的逻辑次序和物理次序之间并无必然联系。最重要的是,链表可以在不移动结点位置的前提下根据需要随时添加删除结点,动态调整。

【数据结构】线性表(二)单链表及其基本操作(创建、插入、删除、修改、遍历打印)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_63834988/article/details/133914875?spm=1001.2014.3001.5501

2. 循环链表

           从单链表的一个结点出发,只能访问到链接在它后面的结点,而无法访问位于它前面的结点,这对一些实际应用很不方便。解决的办法是把链接结构“循环化”,即把表尾结点的next域存放指向哨位结点的指针,而不是存放空指针NULL,这样的单链表被称为循环链表。循环链表使用户可以从链表的任何位置开始,访问链表中的任意结点。

【数据结构】线性表(三)循环链表的各种操作(创建、插入、查找、删除、修改、遍历打印、释放内存空间)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_63834988/article/details/133914085?spm=1001.2014.3001.5501

3. 双向链表

        在循环链表中,从一个结点出发,必须遍历整个链表,方可找到其前驱结点,时间复杂度为O(n) . 双向链表将很好地解决该问题。所谓双向链表,系指链表中任一结点P都是由data域、左指针域left(pre)和右指针域right(next)构成的,左指针域和右指针域分别存放P的左右两边相邻结点的地址信息。

        双向链表的优点是可以在常量时间内删除或插入一个节点,因为只需要修改节点的前后指针,而不需要像单向链表那样遍历到指定位置。而在单向链表中,删除或插入一个节点需要先找到前一个节点,然后修改指针。然而,双向链表相对于单向链表需要更多的内存空间来存储额外的指针。另外,由于多了一个指针,插入和删除节点时需要更多的操作。

a. 双向链表节点结构

typedef struct Node {int data;struct Node* prev;struct Node* next;
} Node;

        包含一个整数data以及两个指针prevnext,分别指向前一个节点和后一个节点。

b. 创建一个新的节点

Node* createNode(int data) {Node* newNode = (Node*)malloc(sizeof(Node));newNode->data = data;newNode->prev = NULL;newNode->next = NULL;return newNode;
}

        创建一个新的节点,它接受一个整数作为参数,并分配内存来存储节点。然后,节点的data被设置为传入的整数,prevnext指针被初始化为NULL,最后返回新创建的节点指针。

c. 在链表末尾插入节点

void append(Node** head, int data) {Node* newNode = createNode(data);if (*head == NULL) {*head = newNode;} else {Node* current = *head;while (current->next != NULL) {current = current->next;}current->next = newNode;newNode->prev = current;}
}
  • 创建一个新的节点,并将传入的整数作为节点的数据。
  • 检查链表是否为空
    • 如果为空,将链表头指针指向新节点;
    • 否则,遍历链表找到最后一个节点,将最后一个节点的next指针指向新节点,新节点的prev指针指向最后一个节点。

d. 在指定位置插入节点

void insert(Node** head, int data, int position) {Node* newNode = createNode(data);if (position == 0) {newNode->next = *head;if (*head != NULL) {(*head)->prev = newNode;}*head = newNode;} else {Node* current = *head;int i;for (i = 0; i < position - 1 && current != NULL; i++) {current = current->next;}if (current == NULL) {printf("Invalid position\n");return;}newNode->next = current->next;newNode->prev = current;if (current->next != NULL) {current->next->prev = newNode;}current->next = newNode;}
}
  • 创建一个新的节点,并将传入的整数作为节点的数据。
  • 如果插入位置为0,表示在链表头插入新节点
    • 将新节点的next指针指向链表的头节点
    • 如果链表不为空,将链表的头节点的prev指针指向新节点,最后将链表的头指针指向新节点。
  • 如果插入位置不为0
    • 首先遍历链表找到插入位置的前一个节点
    • 如果找到了位置或者遍历到链表末尾都没有找到指定位置,则输出"Invalid position"并返回。
    • 否则,将新节点的next指针指向当前节点的next指针所指向的节点,新节点的prev指针指向当前节点
    • 如果当前节点的next指针不为空,将当前节点的next指针所指向的节点的prev指针指向新节点,最后将当前节点的next指针指向新节点。

e. 删除指定位置的节点

void delete(Node** head, int position) {if (*head == NULL) {printf("List is empty\n");return;}Node* temp = *head;if (position == 0) {*head = (*head)->next;if (*head != NULL) {(*head)->prev = NULL;}free(temp);} else {int i;for (i = 0; i < position && temp != NULL; i++) {temp = temp->next;}if (temp == NULL) {printf("Invalid position\n");return;}temp->prev->next = temp->next;if (temp->next != NULL) {temp->next->prev = temp->prev;}free(temp);}
}
  • 如果链表为空,输出"List is empty"并返回。
  • 如果要删除的节点是链表的头节点
    • 将链表的头指针指向头节点的下一个节点,如果链表不为空,将新的头节点的prev指针指向NULL,然后释放被删除的节点的内存。
  • 如果要删除的节点不是头节点
    • 首先遍历链表找到要删除的节点
    • 如果找到了指定位置的节点或者遍历到链表末尾都没有找到,则输出"Invalid position"并返回。
    • 否则,将要删除节点的前一个节点的next指针指向要删除节点的下一个节点,如果要删除节点的下一个节点不为空,将要删除节点的下一个节点的prev指针指向要删除节点的前一个节点。
    • 最后释放要删除节点的内存。

f. 遍历并打印链表

void printList(Node* head) {Node* current = head;while (current != NULL) {printf("%d ", current->data);current = current->next;}printf("\n");
}

        从链表的头节点开始,通过不断访问next指针,打印每个节点的数据,并移动到下一个节点,直到遍历完整个链表。

g. 主函数

int main() {Node* head = NULL;printList(head);// 在链表末尾插入节点append(&head, 1);append(&head, 2);append(&head, 3);printList(head);// 在指定位置插入节点insert(&head, 4, 1);printList(head);// 删除指定位置的节点delete(&head, 2);printList(head);return 0;
}

        

h. 代码整合

#include <stdio.h>
#include <stdlib.h>// 双向链表节点结构
typedef struct Node {int data;struct Node* prev;struct Node* next;
} Node;// 创建一个新的节点
Node* createNode(int data) {Node* newNode = (Node*)malloc(sizeof(Node));newNode->data = data;newNode->prev = NULL;newNode->next = NULL;return newNode;
}// 在链表末尾插入节点
void append(Node** head, int data) {Node* newNode = createNode(data);if (*head == NULL) {*head = newNode;} else {Node* current = *head;while (current->next != NULL) {current = current->next;}current->next = newNode;newNode->prev = current;}
}// 在指定位置插入节点
void insert(Node** head, int data, int position) {Node* newNode = createNode(data);if (position == 0) {newNode->next = *head;if (*head != NULL) {(*head)->prev = newNode;}*head = newNode;} else {Node* current = *head;int i;for (i = 0; i < position - 1 && current != NULL; i++) {current = current->next;}if (current == NULL) {printf("Invalid position\n");return;}newNode->next = current->next;newNode->prev = current;if (current->next != NULL) {current->next->prev = newNode;}current->next = newNode;}
}// 删除指定位置的节点
void delete(Node** head, int position) {if (*head == NULL) {printf("List is empty\n");return;}Node* temp = *head;if (position == 0) {*head = (*head)->next;if (*head != NULL) {(*head)->prev = NULL;}free(temp);} else {int i;for (i = 0; i < position && temp != NULL; i++) {temp = temp->next;}if (temp == NULL) {printf("Invalid position\n");return;}temp->prev->next = temp->next;if (temp->next != NULL) {temp->next->prev = temp->prev;}free(temp);}
}// 遍历并打印链表
void printList(Node* head) {Node* current = head;while (current != NULL) {printf("%d ", current->data);current = current->next;}printf("\n");
}// 主函数
int main() {Node* head = NULL;printList(head);// 在链表末尾插入节点append(&head, 1);append(&head, 2);append(&head, 3);printList(head);// 在指定位置插入节点insert(&head, 4, 1);printList(head);// 删除指定位置的节点delete(&head, 2);printList(head);return 0;
}

五、复杂性分析

        到目前为止,本系列已详细介绍了线性表的两种存储方式——顺序存储和链接存储,下面从空间和时间复杂性两方面对二者进行比较分析。

1. 空间效率比较

        顺序表所占用的空间来自于申请的数组空间,数组大小是事先确定的,很明显当表中的元素较少时,顺序表中的很多空间处于闲置状态,造成了空间的浪费;

        链表所占用的空间是根据需要动态申请的,不存在浪费空间的问题,但是链表需要在每个结点上附加一个指针域,从而增加一定的空间开销。

2. 时间效率比较

        线性表的基本操作是存取、插入和删除。对于顺序表,随机存取是非常容易的,但是每插入或删除一个元素,都需要移动若干元素。

        对于链表,无法实现随机存取,必须要从表头开始遍历链表,直到发现要存取的元素,但是链表的插入和删除操作却非常简便,只需要修改几个指针。

  • 当经常需要对线性表进行插入、删除操作时,链表的时间效率较高;
    • 双向链表在某些场景下更加灵活和高效,特别是需要频繁的插入和删除操作时。然而,在内存有限的情况下,单向链表可能更为合适。
  • 当经常需要对线性表进行存取且存取操作比插入、删除操作更为频繁时,顺序表的时间效率较高

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/113740.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Juniper防火墙SSG-140 session 过高问题

1.SSG-140性能参数 2.问题截图 3.解决方法 &#xff08;1&#xff09;通过telnet 或 consol的方法登录到防火墙&#xff1b; &#xff08;2&#xff09;使用get session 查看总的session会话数&#xff0c;如果大于300 一般属于不正常情况 &#xff08;3&#xff09;使用get…

H5随机短视频滑动版带打赏源码,可封装APP软件或嵌入式观看

H5随机短视频滑动版带打赏源码&#xff0c;可封装APP软件或嵌入式观看&#xff0c;网站引流必备源码&#xff01; 数据来源抖音和快手官方短视频链接&#xff0c;无任何违规内容&#xff01;可自行添加广告等等&#xff01; 手机端完美支持滑动屏幕观看&#xff08;向上或向右…

思维模型 上瘾模型(hook model)

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。你到底是怎么上瘾&#xff08;游戏/抖音&#xff09;的&#xff1f;我们该如何“积极的上瘾”&#xff1f;让我们来一切揭晓这背后的秘密。 1 上瘾模型的应用 1.1上瘾模型的积极应用 1 学…

RT-Thread学习笔记(三):线程管理

线程管理 线程管理相关概念什么是时间片轮转调度器锁线程运行机制线程的五种状态 动态和静态创建线程区别动态和静态创建线程优缺点RT-Thread动态线程管理函数动态创建线程动态删除线程 RT-Thread静态线程管理函数静态创建线程 线程其他操作线程启动线程延时获得当前执行的线程…

四、基本组件

1. Designer设计师 Designer程序是Qt官方推出的专为设计人员使用的UI设计工具&#xff0c;程序员可以使用此工具大幅降低UI设计的代码量。 Designer设计文件的格式是.ui&#xff0c;需要配合同名的头文件与源文件使用。.ui文件通常被称为界面文件&#xff0c;其内部是xml语法的…

微机原理:汇编语言语句类型与格式

文章目录 壹、语句类型1、语句分类2、常用伪代码和运算符2.1数据定义伪指令2.1.1字节定义伪指令DB&#xff08;8位&#xff09;2.1.2字定义伪指令DW&#xff08;16位&#xff09;2.1.3双字节伪指令DD2.1.4 多字节定义DF/DQ/DT&#xff08;了解&#xff09; 2.2 常用运算符2.2.1…

[java进阶]——异常详解,try catch捕获异常,抛出异常

&#x1f308;键盘敲烂&#xff0c;年薪30万&#x1f308; 目录 一、异常的体系结构 二、处理异常的本质 三、异常处理的三种方式 3.1虚拟机jvm处理(默认) 3.2 try catch捕获异常 3.3抛出异常 3.4finally关键字 四、自定义异常 五、总结 一、异常的体系结构 分析&#…

【LeetCode】145. 二叉树的后序遍历 [ 左子树 右子树 根结点]

题目链接 文章目录 Python3方法一&#xff1a; 递归 ⟮ O ( n ) ⟯ \lgroup O(n) \rgroup ⟮O(n)⟯方法二&#xff1a; 迭代 ⟮ O ( n ) ⟯ \lgroup O(n) \rgroup ⟮O(n)⟯方法三&#xff1a; Morris ⟮ O ( n ) 、 O ( 1 ) ⟯ \lgroup O(n)、O(1) \rgroup ⟮O(n)、O(1)⟯写…

MySQL表操作—存储

建表&#xff1a; mysql> create table sch( -> id int primary key, -> name varchar(50) not null, -> glass varchar(50) not null -> ); Query OK, 0 rows affected (0.01 sec) 插入数据&#xff1a; mysql> insert into sch (id,name,…

c语言练习94:分割链表

分割链表 给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你不需要 保留 每个分区中各节点的初始相对位置。 示例 1&#xff1a; 输入&#xff1a;head [1,4,3,2,5,2], x…

短视频矩阵系统源码(搭建)

短视频矩阵源码的开发路径分享如下&#xff1a; 1、首先&#xff0c;确定项目需求和功能&#xff0c;包括用户上传、编辑、播放等。 2、其次&#xff0c;搭建开发环境&#xff0c;选择合适的开发工具和框架。 3、然后&#xff0c;进行项目架构设计和数据库设计&#xff0c;确…

弹出框,使用树结构查询数据

效果如下: 描述:希望点击某个按钮,弹出一个窗口,然后通过下拉框,点击下拉框里面的组织信息,然后查询对应组织的成员对象列表,并展示到表格中 HTML代码(最主要的就是树的那个): <el-dialog :visible.sync="TesteePage.showDialog" width="70%&quo…

【CGSSA-BP预测】基于混合混沌-高斯变异-麻雀算法优化BP神经网络回归预测研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

用Wokwi仿真ESP-IDF项目

陈拓 2023/10/21-2023/10/21 1. 概述 Wokwi是一个在线的电子电路仿真器。你可以使用它来仿真Arduino、ESP32、STM32和许多其他流行的电路板、元器件以及传感器&#xff0c;免去使用开发板。 Wokwi提供基于浏览器的界面&#xff0c;您可以通过这种简单直观的方式快速开发一个…

自然语言处理---Transformer机制详解之Transformer优势

1 Transformer的并行计算 对于Transformer比传统序列模型RNN/LSTM具备优势的第一大原因就是强大的并行计算能力. 对于RNN来说&#xff0c;任意时刻t的输入是时刻t的输入x(t)和上一时刻的隐藏层输出h(t-1)&#xff0c;经过运算后得到当前时刻隐藏层的输出h(t)&#xff0c;这个…

京东数据分析:2023厨房小电市场遇冷,空气炸锅等明星产品被抛弃

过去几年间&#xff0c;宅经济的爆发带火了酸奶机、煮蛋器、豆浆机、空气炸锅、养生壶等&#xff0c;一众外观小巧、功能丰富、价格相对便宜的厨房小家电。但随着年轻人走出家门回归工作岗位&#xff0c;厨房小家电们却步入了艰难时刻。 如今&#xff0c;厨房小家电们似乎正在经…

互联网Java工程师面试题·Java 面试篇·第二弹

目录 15、什么是不可变对象&#xff08;immutable object&#xff09;&#xff1f;Java 中怎么创建一个不可变对象&#xff1f; 16、我们能创建一个包含可变对象的不可变对象吗&#xff1f; 17、Java 中应该使用什么数据类型来代表价格&#xff1f; 18、怎么将 byte 转换为 Str…

网络协议--Ping程序

7.1 引言 “ping”这个名字源于声纳定位操作。Ping程序由Mike Muuss编写&#xff0c;目的是为了测试另一台主机是否可达。该程序发送一份ICMP回显请求报文给主机&#xff0c;并等待返回ICMP回显应答&#xff08;图6-3列出了所有的ICMP报文类型&#xff09;。 一般来说&#x…

超详细 | 差分进化算法原理及其实现(Matlab/Python)

差分进化(Differential Evolution&#xff0c;DE)算法是由美国学者Storn和 Price在1995年为求解Chebyshev多项式拟合问题而提出的。算法主要通过基于差分形式的变异操作和基于概率选择的交叉操作进行优化搜索&#xff0c;虽然其操作名称和遗传算法相同&#xff0c;但实现方法有…

golang 反射机制

在 go 语言中&#xff0c;实现反射能力的是 reflect包&#xff0c;能够让程序操作不同类型的对象。其中&#xff0c;在反射包中有两个非常重要的 类型和 函数&#xff0c;两个函数分别是&#xff1a; reflect.TypeOfreflect.ValueOf 两个类型是 reflect.Type 和 reflect.Value…