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

目录

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

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

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,一经查实,立即删除!

相关文章

LeetCode 2172. 数组的最大与和【状压DP,记忆化搜索;最小费用最大流】2392

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

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…

Python的编辑器VScode中文设置和Hello World

Python的编辑器 个人比较常用的用于Python开发的编辑器是VScode&#xff0c;大概的原因应该是免费&#xff0c;且便于项目文件的管理。 VScode中文设置插件及使用方法 VScode下载安装好之后&#xff0c;可以在软件左侧的“扩展”中搜索安装一些插件&#xff0c;用于辅助开发…

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…

【数据库】SQL 过滤数据

过滤数据 简单过滤where 子句操作符检查单个值范围值检擦空值检查 高级过滤多个过滤条件求值顺序IN 操作符NOT 操作符 在 s q l sql sql 语句中&#xff0c;通过 WHERE 子句指定搜索条件进行过滤。 简单过滤 包含&#xff1a;WHERE&#xff0c;BETWEEN&#xff0c;IS NULL&a…

面向对象与面向过程讲解

目录 简介 面向过程编程&#xff08;Procedural Programming&#xff09; 什么是面向过程编程&#xff1f; 特点&#xff1a; 面向对象编程&#xff08;Object-Oriented Programming&#xff09; 什么是面向对象编程&#xff1f; 特点&#xff1a; 面向对象 vs. 面向过程…

学习人工智能

在线课程 优达学城 当斯坦福大学讲师 Sebastian Thrun 和 Peter Norvig 将他们的课程“人工智能概论”免费放到网上时&#xff0c;Udacity 开始了在线学习的实验。从那时起&#xff0c;它就受到了巨大的欢迎&#xff08;来自 190 多个国家的 160,000 名学生&#xff09;&#x…

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

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

【uniapp】proxy 代理切换至线上测试地址调试接口

本地测试地址形如&#xff1a;http://192.168.124.x:xxxx 线上测试地址形如&#xff1a;https://xxxx.xxxx.com 使用线上地址之后需要修改配置项 secure 为 true const constant require(./src/utils/constant) module.exports {devServer: {proxy: {/api: {target: constan…

Node-EventEmitter的用法

题记 EventEmitter的用法&#xff0c;以下是详细过程和代码。 Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。 Node.js 里面的许多对象都会分发事件&#xff1a;一个 net.Server 对象会在每次有新连接时触发一个事件&#xff0c; 一个 fs.readStream 对象会…

[python-大语言模型]从浅到深一系列学习笔记记录

整体学习路径参照&#xff1a;点这里 python-机器学习-深度学习-大语言模型-数据开发 面向开发者的LLM入门提示原则 面向开发者的LLM入门 学习链接&#xff1a; github地址&#xff1a;https://github.com/datawhalechina/prompt-engineering-for-developers 在线阅读地址&…

【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;确…

PHPExcel 字母列不够用,针对 AA、AB、AC ... ZZ 这样的列

在PHPExcel 导出功能中&#xff0c;如果字段超过26个字母时&#xff0c;会出现字母不够用A~Z后 AA~AZ来添加后续字段 php中&#xff0c;chr() 函数从指定 ASCII 值返回字符&#xff0c;可以自定义一个方法来返回对应的字母 // $num 列数 1,2,3,4,5,6,7...... function getCol…