05 双向链表

目录

1.双向链表
2.实现
3.OJ题
4.链表和顺序表对比


1. 双向链表

前面写了单向链表,复习一下
无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多作为其他数据结构的子结构,如哈希桶、图的邻接等。另外这种结构在笔试面试中出现多

带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构, 都是带头双向循环链表。另外这个结构虽然复杂,但是使用代码实现以后发现会带来很多优势,实现反而简单了

2. 实现

头文件

#pragma once//数据类型
typedef int DataType;
//结构
typedef struct _SListNode
{DataType data;struct _SListNode* pNext;
}SListNode;//插入
void PushFront(SListNode** pHead, DataType data);
void PushBack(SListNode** pHead, DataType data);
//pos之前插入
void Insert(SListNode** pHead, SListNode* pPos, DataType data);
//pos之后插入
void InsertAfter(SListNode** pHead, SListNode* pPos, DataType data);
//查找
SListNode* Find(SListNode* pHead, DataType data);
//删除
void PopFront(SListNode** pHead);
void PopBack(SListNode** pHead);
void Erase(SListNode** pHead, SListNode* pos);
// 删除pos位置后面的值
void EraseAfter(SListNode* pos);//打印
void PrintList(SListNode* pHead);
//销毁
void Destory(SListNode** pHead);

插入只需要修改新节点的前后节点,新节点前后节点的链接,注意顺序,不能覆盖后面需要的值

插入和删除可以复用insert和erase的函数,所以也可以只写这两个,然后来实现头插尾插这些

#include "List.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>List* BuyNode(DATATYPE data)
{List* newnode = (List*)malloc(sizeof(List));if (newnode == NULL){perror("mallco");return NULL;}//初始化数据newnode->data = data;newnode->pre = NULL;newnode->next = NULL;return newnode;
}List* Init()
{List* head = BuyNode(-1);if (head == NULL){perror("mallco");}head->pre = head;head->next = head;return head;
}void PrintList(List* head)
{List* cur = head->next;while (cur != head){printf("->%d ", cur->data);cur = cur->next;}printf("\r\n");
}void PushFront(List* head, DATATYPE data)
{assert(head);//创建节点List* newnode = BuyNode(data);newnode->pre = head;newnode->next = head->next;head->next->pre = newnode;head->next = newnode;/*List* first = head->next;head->next = newnode;newnode->pre = head;newnode->next = first;first->pre = newnode;*///Insert(head->next, data);
}void PushBack(List* head, DATATYPE data)
{assert(head);//创建节点List* newnode = BuyNode(data);List* tail = head->pre;newnode->pre = tail;newnode->next = head;tail->next = newnode;head->pre = newnode;//Insert(head, data);
}void Insert(List* pos, DATATYPE data)
{assert(pos);//创建节点List* newnode = BuyNode(data);List* prev = pos->pre;newnode->pre = pos->pre;newnode->next = pos;prev->next = newnode;pos->pre = newnode;
}void PopFront(List* head)
{assert(head);assert(!Empety(head));List* del = head->next;head->next = del->next;del->next->pre = head;free(del);/*List* first = head->next;List* second = first->next;head->next = second;second->pre = head;free(first);*///erase(head->next)
}void PopBack(List* head)
{assert(head);assert(!Empety(head));List* del = head->pre;//保留尾节点前一个List* tailpre = del->pre;tailpre->next = head;head->pre = tailpre;free(del);//erase(head->pre)
}void Erase(List* pos)
{assert(pos);List* posPre = pos->pre;List* posNext = pos->next;posPre->next = posNext;posNext->pre = posPre;free(pos);
}List* FindNode(List* head, DATATYPE data)
{assert(head);List* cur = head->next;while (cur != head){if (cur->data == data)return cur;cur = cur->next;}return NULL;
}bool Empety(List* head)
{assert(head);return head->next == head;}void Destory(List* head)
{assert(head);List* cur = head->next;while (cur != head){List* next = cur->next;free(cur);cur = next;}free(head);
}

主文件

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "List.h"int main()
{List* list = Init();PushFront(list, 3);PushFront(list, 2);PushFront(list, 1);PushBack(list, 4);PushBack(list, 5);PrintList(list);PopFront(list);PopBack(list);PrintList(list);List* pos = FindNode(list, 3);if (pos != NULL){Insert(pos, 1);PrintList(list);}Erase(pos);PrintList(list);Destory(list);return 0;
}

3. OJ题

链表的复制

https://leetcode.cn/problems/copy-list-with-random-pointer/description/

在这里插入图片描述
思路

直接思路,拷贝一个一模一样的链表,关键是复制random对应的节点值,要遍历看原链表指向的random是在第几个,将新链表的random链接到对应位置,这种方法时间复杂度较高

另一种思路。在原链表每个节点后面插入一个拷贝出来的原链表值。重点在random的链接,这样新链表的random就是原链表对应的rand节点的下一个位置。然后再创建一个新链表,将每个拷贝节点尾插并还原原链表

在这里插入图片描述

如图,原链表指向是1->2->3->null,插入蓝色的新拷贝链表,1->蓝1->2->蓝2->3->蓝3->null,原1的random指向3,那么拷贝链表的random指向就应该是3的next,就是拷贝链表的3

解绑过程如下图

在这里插入图片描述

cur是当前节点,它的next是copy节点,首先链表头和尾本来是空,第一次置为第一个拷贝节点。将copy节点尾插到copytail链表,cur的next节点就是copy节点的下一个,然后将cur更新到copy的next,如下图

在这里插入图片描述这样一直循环

/*** Definition for a Node.* struct Node {*     int val;*     struct Node *next;*     struct Node *random;* };*/struct Node* copyRandomList(struct Node* head) {struct Node* cur = head;//拷贝节点插入在原节点后面while(cur != NULL){struct Node* copy = (struct Node*)malloc(sizeof(struct Node));copy->val = cur->val;struct Node* next = cur->next;//插入cur->next = copy;copy->next = next;cur = next;}//控制拷贝节点的randomcur = head;while(cur != NULL){struct Node* copy = cur->next;if(cur->random == NULL){copy->random = NULL;}else{//指向对应的拷贝链表copy->random = cur->random->next;}cur = copy->next;}//尾插拷贝链表,还原原链表struct Node* copyhead = NULL;struct Node* copytail = NULL;cur = head;while(cur != NULL){struct Node* copy = cur->next;struct Node* next = copy->next;//尾插if(copyhead == NULL){copyhead = copytail = copy;}else{copytail->next = copy;copytail = copytail->next;}//恢复原链表cur->next = copy->next;cur = next;}return copyhead;
}

4. 顺序表和链表对比

不同点顺序表链表
存储空间上物理上一定连续逻辑上连续,物理上不一定
随机访问支持O(1)不支持O(N)
任意位置插入和删除元素可能需要搬元素,O(N)只需修改指针指向
插入动态顺序表,空间不够扩容没有容量概念
应用场景元素高效存储+频繁访问任意位置频繁插入和删除
缓存利用率

链表
优点:
1.任意位置插入删除O(1)
2.按需申请释放空间
缺点:
1.不支持下标随机访问
2.CPU告诉缓存命中率更低
顺序表
优点:
1.尾插尾删效率不错
2.下标的随机访问
3.CPU告诉缓存命中率更高
缺点:
1.除过尾插尾删,效率低O(N),需要挪动元素
2.空间不够,需要扩容
3.扩容需要代价,一般伴随空间浪费

cpu存储分类在这里插入图片描述数据的存储从服务器到硬盘,再到内存。其中内存还有寄存器和告诉缓存部分,访问速度越网上越快,但代价也越高,空间也越小。寄存器中拿数据是最快的,但一般寄存器只有几十字节

内存读取数据并不是需要多少读多少,它会将需要读取的数据后面的一部分也读入缓存中,因为这部分极有可能还会读取,这就是命中缓存,访问速度更快。所以顺序表式连续存储的,命中缓存的几率更高,速度也就更快

每个数据类型都各有优势,有不同的应用场景,不存在优劣

相关参考:
CPU缓存知识

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

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

相关文章

dubbo和eureka的区别

dubbo可以作为客户端&#xff0c;也可以作为服务端&#xff0c;因此他内置了很多序列化框架可供选择&#xff0c;通过配置可以进行选择。默认是hession&#xff0c;还有gson&#xff0c;fastJson&#xff0c;jdk自带的序列化。 eureka只能作为服务端&#xff0c;他序列要与客户…

解析MySQL生产环境CPU使用率过高的排查与解决方案

引言 在生产环境中&#xff0c;MySQL作为一个关键的数据库组件&#xff0c;其性能对整个系统的稳定性至关重要。然而&#xff0c;有时候我们可能会遇到MySQL CPU使用率过高的问题&#xff0c;这可能导致系统性能下降&#xff0c;应用页面访问减慢&#xff0c;甚至影响到用户体…

软件包管理:在CentOS 7中部署Tengine

目录 下载&#xff1a; 方法一&#xff1a; 方法二&#xff1a; 部署&#xff1a; 实验操作 下载&#xff1a; 方法一&#xff1a; 1、打开浏览器搜索tengine并点击官网 2、选择需要安装的版本并复制链接链接 标题栏处可以更改为中文界面 下滑选择版本单击下载 在远程连…

Python字符串:基础要点与实践应用

文章目录 一、Python字符串1.介绍2.与C语言字符串比较2.1 相同点2.2 不同点 3.创建Python字符串3.1 使用单引号3.2 使用双引号3.3 使用三引号 二、访问字符串中的值1.索引方式2.截取方式 三、Python 转义字符1.续行符\(在行尾时)2.反斜杠符号\\3.单引号\4.双引号\"5.响铃\…

使用Docker部署MySQL并结合内网穿透实现远程访问本地数据库

文章目录 前言1 .安装Docker2. 使用Docker拉取MySQL镜像3. 创建并启动MySQL容器4. 本地连接测试4.1 安装MySQL图形化界面工具4.2 使用MySQL Workbench连接测试 5. 公网远程访问本地MySQL5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定TCP地址远程访问 前言 本文主…

搭建nginx图片服务器

&#xff08;1&#xff09;将图片存储于/home/data/images目录&#xff1b; &#xff08;2&#xff09;配置nginx.conf user nginx; worker_processes 4;error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid;events {worker_connections 10000; }ht…

Vue3中ElementPlus组件二次封装,实现原组件属性、插槽、事件监听、方法的透传

本文以el-input组件为例&#xff0c;其它组件类似用法。 一、解决数据绑定问题 封装组件的第一步&#xff0c;要解决的就是数据绑定的问题&#xff0c;由于prop数据流是单向传递的&#xff0c;数据只能从父流向子&#xff0c;子想改父只能通过提交emit事件通知父修改。 父&a…

移动Web——平面转换-旋转

1、平面转换-旋转 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style…

在使用springboot框架式的的script无法通过${}来获取值

今天使用springboot框架做项目&#xff0c;想着来实现一下搜索的下拉框回显功能&#xff0c;然后就一直在报错误&#xff0c;关键是报的错误牛头不对马嘴&#xff0c;检查了一下后端代码&#xff0c;发现没什么问题&#xff0c;就把目光聚焦了.jsp页面的代码 <script type&…

主流影视网站8合一H5源码

目前影视接口完好&#xff0c;可正常观看影视。 上传即可使用 包括了 百度视频风格 PP视频风格 咪咕爱看风格 爱奇艺风格 腾讯视频风格 优酷视频风格 搜狐视频风格 B站风格 8种主流影视网站&#xff0c;喜欢那个用那个

【STM32】STM32学习笔记-Unix时间戳(41)

00. 目录 文章目录 00. 目录01. Unix时间戳02. UTC/GMT03. 时间戳转换04. C 标准库 <time.h>05. 时间相关函数示例5.1 time函数5.2 gmtime函数5.3 localtime函数5.4 mktime函数5.5 ctime函数5.6 asctime函数5.7 strftime函数 06. 预留07. 附录 01. Unix时间戳 •Unix 时…

2024-macOS系统或Kail系统重——破解ZIP压缩的文件密码

2024-macOS系统或Kail系统重——破解ZIP压缩的文件密码 1. 你们有遇见这样子的情况么&#xff1a; 别人给你发的zip或者下载的zip文件&#xff0c;没有密码打不开么网上都是win系统的&#xff0c;都是没有macOS系统的&#xff0c;所以比较烦恼 2. 所以我就想到了代码&#x…

gradle简单入门

安装 需要有Java环境 下载地址&#xff1a;https://gradle.org/releases/ 8.5版本仅有二进制文件&#xff1a;https://gradle.org/next-steps/?version8.5&formatbin 8.5版本包含文档和源码及二进制文件&#xff1a;https://gradle.org/next-steps/?version8.5&f…

无线路由探索

实验大纲 第一部分&#xff1a; 探索无线网络 步骤 1&#xff1a; 探索拓扑 步骤 2&#xff1a; 验证连接 第二部分&#xff1a; Wi-Fi 连接添加到董事会议室 步骤 1&#xff1a; 安装新的 LAP-PT 设备以覆盖新的董事会议室 步骤 2&#xff1a; 检验连接 第三部分&#…

Git初识

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 在学习Git之前我们先引入一…

C/C++ - 面向对象编程

面向对象 面向过程编程&#xff1a; 数据和函数分离&#xff1a;在C语言中&#xff0c;数据和函数是分开定义和操作的。数据是通过全局变量或传递给函数的参数来传递的&#xff0c;函数则独立于数据。函数为主导&#xff1a;C语言以函数为主导&#xff0c;程序的执行流程由函数…

外卖跑腿系统开发:构建高效、安全的服务平台

在当今快节奏的生活中&#xff0c;外卖跑腿系统的开发已成为技术领域的一个重要课题。本文将介绍如何使用一些常见的编程语言和技术框架&#xff0c;构建一个高效、安全的外卖跑腿系统。 1. 技术选择 在开始开发之前&#xff0c;我们需要选择适合的技术栈。常用的技术包括&a…

DDT数据驱动测试

简单介绍 ​ DDT&#xff08;Date Driver Test&#xff09;&#xff0c;所谓数据驱动测试&#xff0c; 简单来说就是由数据的改变从而驱动自动化测试的执行&#xff0c;最终引起测试结果的改变。通过使用数据驱动测试的方法&#xff0c;可以在需要验证多组数据测试场景中&…

详细分析Java的树形工具类(含注释)

目录 前言1. 基本框架2. 实战应用 前言 对应的每个子孙属于该父亲&#xff0c;这其实是数据结构的基础知识&#xff0c;那怎么划分怎么归属呢 对应的基本知识推荐如下&#xff1a; 【数据结构】树和二叉树详细分析&#xff08;全&#xff09;【数据结构】B树和B树的笔记详细…

数据结构与算法教程,数据结构C语言版教程!(第六部分、数据结构树,树存储结构详解)二

第六部分、数据结构树&#xff0c;树存储结构详解 数据结构的树存储结构&#xff0c;常用于存储逻辑关系为 "一对多" 的数据。 树存储结构中&#xff0c;最常用的还是二叉树&#xff0c;本章就二叉树的存储结构、二叉树的前序、中序、后序以及层次遍历、线索二叉树、…