04 双向链表

目录

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

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

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

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

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

相关文章

windows用mingw(g++)编译opencv,opencv_contrib,并install安装

windows下用mingw编译opencv貌似不支持cuda&#xff0c;选cuda会报错&#xff0c;我无法解决&#xff0c;所以没选cuda&#xff0c;下面两种编译方式支持。 如要用msvc编译opencv&#xff0c;参考我另外一篇文章 https://blog.csdn.net/weixin_44733606/article/details/1357…

初识人工智能,一文读懂机器学习之逻辑回归知识文集(1)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

进击的 Serverless:Java 应用如何从容地面对突增流量

作者&#xff1a;袭周、十眠 让我们从一个十分常见的业务场景说起。 移动互联网时代&#xff0c;许许多多的业务都有着流量的周期性变化&#xff0c;无论是直播、游戏、点单系统、电商平台等等&#xff0c;都会存在着流量的高峰与波谷。如果采用固定的计算资源部署服务&#…

sqli-labs通关笔记(less-11 ~ less16)

上一篇文章说了sqli-labs的less-1到less-10的注入方法&#xff0c;这一篇从less-11开始。 由于从11关开始都是post请求&#xff0c;不会像前十关一样存在符号转成url编码的麻烦&#xff0c;所以不再使用apifox&#xff0c;直接从页面上进行测试。 Less-11 老规矩&#xff0c;…

解决vue 2.6通过花生壳ddsn(内网穿透)实时开发报错Invalid Host header和websocket

请先核对自己的vue版本&#xff0c;我的是2.6.14&#xff0c;其他版本未测试 起因 这两天在维护一个基于高德显示多个目标&#xff08;门店&#xff09;位置的项目&#xff0c;由于高德要求定位必须使用https服务&#xff0c;遂在本地无法获取到定位坐标信息&#xff0c;于是…

JVM实战篇:GC调优

目录 一.GC调优的核心指标 1.1吞吐量&#xff08;Throughput&#xff09; 1.2延迟&#xff08;Latency&#xff09; 1.3内存使用量 二.GC调优的方法 2.1监控工具 Jstat工具 VisualVm插件 Prometheus Grafana 2.2诊断原因 GC日志 GC Viewer GCeasy 2.3常见的GC模…

手把手教你开发第一个HarmonyOS (鸿蒙)移动应用

⼀、移动应⽤开发的介绍 移动应⽤开发: AndroidIOSHarmonyOS &#xff08;鸿蒙&#xff09; ⼆、HarmonyOS介绍 文档概览-HarmonyOS应用开发官网 2.1 系统的定义 2.1.1 系统的定位 HarmonyOS有三⼤特征&#xff1a; 搭载该操作系统的设备在系统层⾯融为⼀体、形成超级终…

opencv#27模板匹配

图像模板匹配原理 例如给定一张图片&#xff0c;如上图大矩阵所示&#xff0c;然后给定一张模板图像&#xff0c;如上图小矩阵。 我们在大图像中去搜索与小图像中相同的部分或者是最为相似的内容。比如我们在图像中以灰色区域给出一个与模板图像尺寸大小一致的区域&#xff0c;…

CRM的定义、功能,以及国内外CRM系统排名

什么是客户关系管理? CRM是(客户关系管理)的缩写&#xff0c;是一个管理与客户关系的系统。CRM的主要功能是管理基本客户信息和购买历史的客户管理、分析潜在客户和新客户的客户分析、对询问的自动回复的响应以及通过电子邮件通讯和研讨会吸引客户。它是加强和维护与客户和潜…

opencv#30 线性滤波

均值滤波原理 均值滤波步骤 Step1:求和。 Step2:计算平均值。 所谓均值滤波&#xff0c;就是求平均值的意思。我们假设在一个3*3的范围内有一个图像&#xff0c;其中这个图像每一个像素可能含有噪声&#xff0c;也可能不含噪声&#xff0c;我们是不知道的&#xff0c;因此通…

持续集成工具Jenkins的使用之配置篇(二)

上一篇 &#xff1a;持续集成工具Jenkins的安装配置之安装篇(一)-CSDN博客 接上文 三.Jenkins配置 Jenkins配置主要是针对创建构建任务前做的一些基本配置&#xff0c;这些配置有些是必须的&#xff0c;有些是可以帮我们提高效率的&#xff0c;总之都是Jenkins管理员都要会的…

从物联网看智慧文旅的未来:技术与实践的完美结合,重塑旅游体验的新篇章

一、物联网技术&#xff1a;智慧文旅的基石 随着科技的飞速发展&#xff0c;物联网技术已经深入到我们生活的方方面面&#xff0c;尤其在智慧文旅领域&#xff0c;物联网技术更是起到了不可或缺的作用。它如同智慧文旅的基石&#xff0c;为旅游行业带来了前所未有的创新和变革…

VRRP6协议--负载均衡配置

VRRP6负载均衡 VRRP6负载均衡指的是创建多个备份组,多个备份组同时承担数据转发的任务,对于每一个备份组,都有自己的Master和若干Backup设备。 VRRP6负载分担与VRRP6主备备份的基本原理和报文协商过程都是相同的。同样对于每一个VRRP6备份组,都包含一个Master设备和若干Ba…

统计中集中趋势的衡量标准

一、说明 统计中的中心趋势是用于表示大量数值数据的中间值或中心值的数值。这些获得的数值在统计学中称为中心值或平均值。 任何统计数据或序列的中心值或平均值是代表整个数据或其相关频率分布的变量的值。这样的值具有重要意义&#xff0c;因为它描绘了整个数据的性质或特征…

针对特定领域较小的语言模型是否与较大的模型同样有效?

经过2023年的发展&#xff0c;大语言模型展示出了非常大的潜力&#xff0c;训练越来越大的模型成为有效性评估的一个关键指标&#xff0c;论文《A Comparative Analysis of Fine-Tuned LLMs and Few-Shot Learning of LLMs for Financial Sentiment Analysis》中&#xff0c;作…

阿里云 SAE 2.0 正式商用:极简易用、百毫秒弹性效率,降本 40%

作者&#xff1a;黛忻 本文主要介绍阿里云 Serverless 应用引擎&#xff08;以下简称 SAE &#xff09;如何帮助企业跨越技术鸿沟&#xff0c;从传统应用架构无感升级到 Serverless 架构&#xff0c;以更高效、更经济的方式进行转型&#xff0c;快速进入云原生快车道&#xff0…

【Android】TypedArray的使用

介绍 看电池电量组件BatteryMeterView的时候看到的。 Array是个数组&#xff0c;所有TypedArray也是个容器&#xff0c;基本是用于自定义View里面的&#xff08;至少我目前见过的全部都在自定义View里面&#xff09;。 使用 1.自定义View public class RoundSeekbarView e…

【深度学习】BasicSR训练过程记录,如何使用BasicSR训练GAN

文章目录 两种灵活的使用场景项目结构概览简化的使用方式 项目结构解读1. 代码的入口和训练的准备工作2. data和model的创建2.1 dataloader创建2.2 model的创建 3. 训练过程 动态实例化的历史演进1. If-else判断2. 动态实例化3. REGISTER注册机制 REGISTER注册机制的实现1. DAT…

嵌入式基础知识-测试基础概念

本篇来介绍嵌入式项目开发中&#xff0c;软件测试的相关基础知识。 1 测试基础知识 测试是指&#xff1a;在规定的条件下对程序进行操作&#xff0c;以发现错误&#xff0c;对软件质量进行评估 测试的对象包括程序、数据和文档 对于测试&#xff0c;并不是只有测试人员才需…

读AI3.0笔记04_视觉识别

1. 两次飞跃 1.1. ConvNets是当今计算机视觉领域深度学习革命的驱动力 1.1.1. 20世纪80年代便由法国计算机科学家杨立昆提出&#xff0c;而他则是受到了福岛邦彦提出的神经认知机&#xff08;Neocognitron&#xff09;的启发 1.2. ImageNet竞赛被看作计算机视觉和人工智能进…