当前位置: 首页 > news >正文

【中等】707.设计链表

题目描述

你可以选择使用单链表或者双链表,设计并实现自己的链表。

单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。

如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。

实现 MyLinkedList 类:

  • MyLinkedList() 初始化MyLinkedList对象。
  • int get(int index) 获取链表中下标为index的节点的值。如果下标无效,则返回 -1
  • void addAtHead(int val) 将一个值为val的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
  • void addAtTail(int val) 将一个值为val的节点追加到链表中作为链表的最后一个元素。
  • void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
  • void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。

示例:

输入
["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]

解释

MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2);    // 链表变为 1->2->3
myLinkedList.get(1);              // 返回 2
myLinkedList.deleteAtIndex(1);    // 现在,链表变为 1->3
myLinkedList.get(1);              // 返回 3

解决方法

视频链接:代码随想录:707.设计链表
在这里插入图片描述
这道题目设计链表的五个接口:

获取链表第index个节点的数值
在链表的最前面插入一个节点
在链表的最后面插入一个节点
在链表第index个节点前面插入一个节点
删除链表的第index个节点
可以说这五个接口,已经覆盖了链表的常见操作,是练习链表操作非常好的一道题目

可参考代码随想录

方法一:虚拟头节点

设置虚拟头结点,让原来的头结点和后面的结点的处理方式一致,更方便

class MyLinkedList {
public:// 定义链表结点结构体struct LinkedNode{int val;LinkedNode* next;LinkedNode(int val):val(val), next(nullptr){}};//初始化链表MyLinkedList() {_dummyHead = new LinkedNode(0); //定义虚拟头结点_size = 0;}// 获取到第index个结点数值,如果非法,返回-1  从0开始int get(int index) {if(index > (_size - 1) || index < 0){return -1;}LinkedNode* cur = _dummyHead->next;while(index--)      //如果--index就会陷入死循环{cur = cur->next;}return cur->val; }// 在链表最前面插入一个节点,插入完成后,新插入的节点为链表的新的头结点void addAtHead(int val) {LinkedNode* newNode = new LinkedNode(val);newNode->next = _dummyHead->next;_dummyHead->next = newNode;_size++;}// 在链表最后面添加一个节点void addAtTail(int val) {LinkedNode* newNode = new LinkedNode(val);LinkedNode* cur = _dummyHead;while(cur->next != nullptr){cur = cur->next;}cur->next = newNode;_size++;}// 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。// 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点// 如果index大于链表的长度,则返回空// 如果index小于0,则在头部插入节点void addAtIndex(int index, int val) {if(index > _size){return;}    if(index < 0){index = 0;}LinkedNode* newNode = new LinkedNode(val);LinkedNode* cur = _dummyHead;while(index--){cur = cur->next;}newNode->next = cur->next;cur->next = newNode;_size++;}// 删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的void deleteAtIndex(int index) {if(index >= _size || index < 0){return;}LinkedNode* cur = _dummyHead;while(index--){cur = cur->next;}LinkedNode* tmp = cur->next;cur->next = cur->next->next;delete tmp;tmp = nullptr;_size--;}private:LinkedNode* _dummyHead;     //声明虚拟头结点int _size;  //声明链表长度};

时间复杂度: 涉及 index 的相关操作为 O(index), 其余为 O(1)
空间复杂度: O(n)

方法二:双向链表法

双指针,一个指向下一结点,一个指向上一结点,更方便插入

class MyLinkedList {
public:// 定义双向链表结点结构体struct DList{int elem;DList *next;DList *prev;DList(int elem):elem(elem), next(nullptr), prev(nullptr){};};//初始化链表MyLinkedList() {sentinelNode = new DList(0);    // 创建哨兵节点,不存储有效数据sentinelNode->next = sentinelNode; // 哨兵节点的下一个节点指向自身,形成循环sentinelNode->prev = sentinelNode; // 哨兵节点的上一个节点指向自身,形成循环size = 0;}// 获取到第index个结点数值,如果非法,返回-1  从0开始int get(int index) {if(index > (size - 1) || index < 0){return -1;}int num;int mid = size >> 1;  // 计算链表中部位置DList * cur = sentinelNode;     // 从哨兵节点开始if(index < mid) // 如果索引小于中部位置,从前往后遍历{for(int i = 0; i < index + 1; i++){cur = cur->next;}}else // 如果索引大于等于中部位置,从后往前遍历{for(int i = 0; i < size - index; i++){cur = cur->prev;}}num = cur->elem;return num;}// 在链表最前面插入一个节点,插入完成后,新插入的节点为链表的新的头结点void addAtHead(int val) {DList *newNode = new DList(val);DList *next = sentinelNode->next;   // 获取当前头节点的下一个节点newNode->prev = sentinelNode;   // 新节点的上一个节点指向哨兵节点newNode->next = next;    // 新节点的下一个节点指向原来的头节点size++;sentinelNode->next = newNode;   // 哨兵节点的下一个节点指向新节点next->prev = newNode;    // 原来的头节点的上一个节点指向新节点}// 在链表最后面添加一个节点void addAtTail(int val) {DList *newNode = new DList(val);DList *prev = sentinelNode->prev;    // 获取当前尾节点的上一个节点newNode->next = sentinelNode;   // 新节点的下一个节点指向哨兵节点newNode->prev = prev;    // 新节点的上一个节点指向原来的尾节点size++;sentinelNode->prev = newNode;    // 哨兵节点的上一个节点指向新节点prev->next = newNode;    // 原来的尾节点的下一个节点指向新节点}// 在链表中的第index个节点之前添加值为val的节点void addAtIndex(int index, int val) {if(index > size){return;}if(index <= 0){addAtHead(val); // 如果索引为0或负数,在头部添加节点return;}int mid = size >> 1;    // 计算链表中部位置DList *cur = sentinelNode;   // 从哨兵节点开始if(index < mid) // 如果索引小于中部位置,从前往后遍历{for(int i = 0; i < index; i++){cur = cur->next; // 移动到目标位置的前一个节点}DList *tmp = cur->next;     // 获取目标位置的节点DList *newNode = new DList(val);     // 创建新节点cur->next = newNode;     // 在目标位置前添加新节点tmp->prev = newNode;    // 目标位置的节点的前一个节点指向新节点newNode->next = tmp;     // 新节点的下一个节点指向目标位置的结点newNode->prev = cur;    // 新节点的上一个节点指向当前节点}else // 如果索引大于等于中部位置,从后往前遍历{for(int i = 0; i < size - index; i++){cur = cur->prev; // 移动到目标位置的后一个节点}DList *tmp = cur->prev; // 获取目标位置的节点DList *newNode = new DList(val); // 创建新节点cur->prev = newNode; // 在目标位置后添加新节点tmp->next = newNode; // 目标位置的节点的下一个节点指向新节点newNode->prev = tmp; // 新节点的上一个节点指向目标位置的节点newNode->next = cur; // 新节点的下一个节点指向当前节点}size++; // 链表大小加1}// 删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的void deleteAtIndex(int index) {if(index > (size - 1) || index < 0){return;}int num;int mid = size >> 1; // 计算链表中部位置DList *cur = sentinelNode;// 从哨兵节点开始if(index < mid){for(int i = 0; i< index; i++){cur = cur->next;}DList *next = cur->next->next; // 获取目标位置的下一个节点cur->next = next; // 删除目标位置的节点next->prev = cur; // 目标位置的下一个节点的前一个节点指向当前节点}else{for(int i = 0; i < size - index - 1; i++){cur = cur->prev;}DList *prev = cur->prev->prev;cur->prev = prev;prev->next = cur;}size--;}private:DList* sentinelNode;     //声明虚拟头结点int size;  //声明链表长度};

相关文章:

【中等】707.设计链表

题目描述 你可以选择使用单链表或者双链表&#xff0c;设计并实现自己的链表。 单链表中的节点应该具备两个属性&#xff1a;val 和 next 。val 是当前节点的值&#xff0c;next 是指向下一个节点的指针/引用。 如果是双向链表&#xff0c;则还需要属性 prev 以指示链表中的…...

深入理解Reactor Flux的生成方法

在Reactor框架中&#xff0c;Flux 是一个非常重要的概念&#xff0c;它用于表示一个可以产生多个事件的响应式流。通过 Flux 提供的多种生成方法&#xff0c;我们可以灵活地创建各种类型的流。本文将详细介绍 Flux.generate 方法的使用&#xff0c;并通过实例帮助读者更好地理解…...

next实现原理

Next.js 是一个基于 React 的 服务器端渲染&#xff08;SSR&#xff09; 和 静态生成&#xff08;SSG&#xff09; 框架&#xff0c;它的实现原理涉及多个关键技术点&#xff0c;包括 服务端渲染&#xff08;SSR&#xff09;、静态生成&#xff08;SSG&#xff09;、客户端渲染…...

LeetCode 热题 100 53. 最大子数组和

LeetCode 热题 100 | 53. 最大子数组和 大家好&#xff0c;今天我们来解决一道经典的算法题——最大子数组和。这道题在 LeetCode 上被标记为中等难度&#xff0c;要求我们找出一个具有最大和的连续子数组&#xff0c;并返回其最大和。下面我将详细讲解解题思路&#xff0c;并…...

DeepSeek 与大数据治理:AI 赋能数据管理的未来

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 1. 引言 在当今数字化时代&#xff0c;数据已成为企业和机构的重要资产&#xff0c;而大数据治理&#xff08;Big Data Governan…...

【时时三省】(C语言基础)浮点型数据

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 浮点型数据 浮点型数据是用来表示具有小数点的实数的&#xff0c;为什么在C中把实数称为浮点数呢?在C语言中&#xff0c;实数是以指数正式存放在在储单元中的。一个实数表示为指数可以有不…...

【大模型】Ollama本地部署DeepSeek大模型:打造专属AI助手

【大模型】Ollama本地部署DeepSeek大模型&#xff1a;打造专属AI助手 Ollama本地部署DeepSeek大模型&#xff1a;打造专属AI助手一、Ollama简介二、硬件需求三、部署步骤1. 下载并安装Ollama&#xff08;1&#xff09;访问Ollama官网&#xff08;2&#xff09;安装Ollama 2. 配…...

2025.3.2机器学习笔记:PINN文献阅读

2025.3.2周报 一、文献阅读题目信息摘要Abstract创新点网络架构实验结论不足以及展望 一、文献阅读 题目信息 题目&#xff1a; Physics-Informed Neural Networks of the Saint-Venant Equations for Downscaling a Large-Scale River Model期刊&#xff1a; Water Resource…...

数据集笔记:新加坡 地铁(MRT)和轻轨(LRT)票价

数据连接 data.gov.sg 2024 年 12 月 28 日起生效的新加坡地铁票价 该数据集包含 MRT 和 LRT 票价的信息&#xff0c;包括&#xff1a; 票价类型&#xff08;Fare Type&#xff09;&#xff1a;成人票、学生票、老年人票、残障人士票等。适用时间&#xff08;Applicable Tim…...

如何修改安全帽/反光衣检测AI边缘计算智能分析网关V4的IP地址?

TSINGSEE青犀推出的智能分析网关V4&#xff0c;是一款集成了BM1684芯片的高性能AI边缘计算智能硬件。其内置的高性能8核ARM A53处理器&#xff0c;主频可高达2.3GHz&#xff0c;INT8峰值算力更是达到了惊人的17.6Tops。此外&#xff0c;该硬件还预装了近40种AI算法模型&#xf…...

Java 大视界 -- 基于 Java 的大数据分布式缓存一致性维护策略解析(109)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…...

SyntaxError: positional argument follows keyword argument

命令行里面日常练手爬虫不注意遇到的问题&#xff0c;报错说参数位置不正确 修改代码后&#xff0c;运行如下图&#xff1a; 结果&#xff1a; 希望各位也能顺利解决问题&#xff0c;祝你好运&#xff01;...

Ruby基础

一、字符串 定义 283.to_s //转为string "something#{a}" //定义字符串&#xff0c;并且插入a变量的值 something//单引号定义变量 %q(aaaaaaaaa) // 定义字符串&#xff0c;&#xff08;&#xff09;内可以是任何数&#xff0c;自动转义双引号%Q("aaaaa"…...

JMeter 断言最佳实践

JMeter 断言最佳实践 一、引言 在使用 JMeter 进行性能测试或功能测试时&#xff0c;断言是非常重要的一部分。断言可以帮助我们验证接口返回的结果是否符合预期&#xff0c;确保测试的准确性和可靠性。本文将介绍 JMeter 中常见的断言类型、使用这些断言的最佳实践&#xff…...

【Android】类加载器热修复-随记(二)

1. 背景 在【Android】类加载器&热修复-随记一文中了解了类加载,要完成完整的热修复过程,我们需要构建出差量jar包。而这构建差量包分为两个步骤: 原包,注解解析和插桩;变更后,差量包构建;在这两步过程中会涉及到较多的字节码操作,这里我们需要了解下。我们都听过…...

从零开始用react + tailwindcss + express + mongodb实现一个聊天程序(八) 聊天框用户列表

简单画了个聊天框 就是咱们的HomePage.jsx 1.后端接口开发 在server/src/index.js 新增 messagesRoutes 先引入 import messageRoutes from ./routes/message.route.js // 消息接口 app.use(/api/messages, messageRoutes) 在routes文件夹下新建message.route.js 有3个路…...

Linux网络 TCP全连接队列与tcpdump抓包

TCP全连接队列 在 Linux 网络中&#xff0c;TCP 全连接队列&#xff08;也称为 Accept 队列&#xff09;是一个重要的概念&#xff0c;用于管理已经完成三次握手&#xff0c;即已经处于 established 状态但尚未被应用程序通过 accept( ) 函数处理的 TCP 连接&#xff0c;避免因…...

水滴tabbar canvas实现思路

废话不多说之间看效果图,只要解决了这个效果水滴tabbar就能做出来了 源码地址 一、核心实现步骤分解 布局结构搭建 使用 作为绘制容器 设置 width=600, height=200 基础尺寸 通过 JS 动态计算实际尺寸(适配高清屏) function initCanvas() {// 获取设备像素比(解决 Re…...

鸿蒙通过用户首选项实现数据持久化

鸿蒙通过用户首选项实现数据持久化 1.1 场景介绍 用户首选项为应用提供Key-Value键值型的数据处理能力&#xff0c;支持应用持久化轻量级数据&#xff0c;并对其修改和查询。当用户希望有一个全局唯一存储的地方&#xff0c;可以采用用户首选项来进行存储。Preferences会将该…...

在Ubuntu中,某个文件的右下角有一把锁的标志是什么意思?

在Ubuntu中&#xff0c;某个文件的右下角有一把锁的标志是什么意思&#xff1f; 在 Ubuntu&#xff08;或其他基于 GNOME 文件管理器的 Linux 发行版&#xff09;中&#xff0c;文件或文件夹的右下角出现一把“锁”标志&#xff0c;通常表示 你当前的用户没有该文件/文件夹的写…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

Java数组Arrays操作全攻略

Arrays类的概述 Java中的Arrays类位于java.util包中&#xff0c;提供了一系列静态方法用于操作数组&#xff08;如排序、搜索、填充、比较等&#xff09;。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序&#xff08;sort&#xff09; 对数组进行升序…...

MeshGPT 笔记

[2311.15475] MeshGPT: Generating Triangle Meshes with Decoder-Only Transformers https://library.scholarcy.com/try 真正意义上的AI生成三维模型MESHGPT来袭&#xff01;_哔哩哔哩_bilibili GitHub - lucidrains/meshgpt-pytorch: Implementation of MeshGPT, SOTA Me…...

欢乐熊大话蓝牙知识17:多连接 BLE 怎么设计服务不会乱?分层思维来救场!

多连接 BLE 怎么设计服务不会乱&#xff1f;分层思维来救场&#xff01; 作者按&#xff1a; 你是不是也遇到过 BLE 多连接时&#xff0c;调试现场像网吧“掉线风暴”&#xff1f; 温度传感器连上了&#xff0c;心率带丢了&#xff1b;一边 OTA 更新&#xff0c;一边通知卡壳。…...