双链表——AcWing.827双链表

2024-06-16 14:36
文章标签 双链 acwing.827

本文主要是介绍双链表——AcWing.827双链表,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

双链表

定义

双链表是链表的一种,它的每个节点有两个指针,一个指向前一个节点,一个指向后一个节点。这样使得链表可以双向遍历。

运用情况

  • 频繁进行前后双向遍历操作时非常有用,比如在一些需要来回移动处理数据的场景。
  • 可以方便地实现诸如栈、队列等数据结构的混合操作。
  • 在一些需要快速插入和删除节点,同时又需要双向遍历的特定算法和程序中经常被采用。

注意事项

  • 内存管理要恰当,确保正确地分配和释放节点内存,避免内存泄漏或非法访问。
  • 对节点指针的操作要格外小心,防止出现悬空指针或错误指向。
  • 在进行插入、删除等操作时,要注意前后节点指针的正确更新,保持链表的完整性。

解题思路

当使用双链表解决问题时,一般思路如下:

  • 明确问题中涉及到的操作,比如插入、删除、遍历等。
  • 根据操作确定如何更新节点的前后指针以维持链表结构。
  • 在进行复杂操作时,仔细考虑边界情况和特殊情况,确保算法的正确性和鲁棒性。
  • 可以借助一些辅助指针或变量来方便地进行节点的定位和操作。例如,在寻找特定节点时,可以利用前后向的遍历。
  • 对于一些需要高效操作的场景,优化指针的操作顺序和算法流程以提高性能。

AcWing.827双链表

题目描述

827. 双链表 - AcWing题库

运行代码

#include <iostream>
using namespace std;
const int N = 100010;
int l[N], r[N], idx, e[N];
int n;
void init()
{r[0] = 1, l[1] = 0, idx = 2;
}
void insert(int k, int x)
{e[idx] = x;l[idx] = k, r[idx] = r[k];l[r[k]] = idx, r[k] = idx ++ ;
}
void Remove(int k)
{l[r[k]] = l[k];r[l[k]] = r[k];
}
int main()
{init();cin >> n;while(n -- ){string op;int k, x;cin >> op;if(op == "L"){cin >> x;insert(0, x);}else if(op == "R"){cin >> x;insert(l[1], x);}else if(op == "IL"){cin >> k >> x;insert(l[k + 1], x);}else if(op == "IR"){cin >> k >> x;insert(k + 1, x);}else {cin >> k;Remove(k + 1);}}for(int i = r[0]; i != 1; i = r[i]) cout << e[i] << ' ';    return 0;
}

代码思路

  1. 初始化:
    • 使用两个数组lr来分别存储每个节点的左指针和右指针。
    • 数组e用于存储每个节点的值。
    • idx用于追踪下一个要插入的节点的索引。
    • init()函数初始化双链表,将第一个虚拟节点(通常用作头节点)的右指针指向第二个虚拟节点(通常用作尾节点的前一个节点),并将第二个虚拟节点的左指针指向第一个虚拟节点。
  2. 插入操作:
    • insert(int k, int x)函数用于在给定节点k的右侧插入一个新节点,其值为x
    • 首先,将新节点的值存储在e[idx]中。
    • 然后,更新l[idx]r[idx]以指向正确的邻居节点。
    • 同时,更新k的右邻居和idx的左邻居的指针,使其指向新节点。
    • 最后,递增idx以准备下一次插入。
  3. 删除操作:
    • Remove(int k)函数用于删除给定节点k(注意:这里的k是节点在数组中的索引,而不是节点值)。
    • 通过更新k的左邻居的右指针和k的右邻居的左指针,来删除节点k
    • 注意:这里没有显式地释放或重置数组e中的值,因为C++的数组不会自动管理内存。但由于e是静态分配的,它将在程序结束时自动被销毁。
  4. 主函数:
    • 读取一个整数n,表示将要进行的操作数。
    • 对于每个操作,读取一个操作码op和一个或两个参数(取决于操作)。
    • 根据操作码执行相应的操作:
      • "L": 在双链表的左侧插入一个新节点。
      • "R": 在双链表的右侧插入一个新节点。
      • "IL": 在第k个节点的左侧插入一个新节点。
      • "IR": 在第k个节点的右侧插入一个新节点。
      • 其他: 删除第k个节点。
    • 在所有操作完成后,遍历并打印双链表中的节点值。

注意

  • 这里的节点索引是从虚拟头节点(索引为0)和虚拟尾节点的前一个节点(索引为1)开始的。因此,当插入或删除节点时,需要使用k + 1来引用实际的节点索引(从2开始)。
  • 代码没有包含错误检查,例如检查k是否超出了链表的合法范围。在实际应用中,应添加这些检查以防止程序崩溃或产生不可预测的行为。

改进思路

  1. 添加错误处理
    • 在进行插入或删除操作时,检查给定的索引k是否在链表的有效范围内。
    • 检查操作是否符合预期,比如尝试在已经不存在的节点上进行操作。
  2. 使用结构体或类
    • 创建一个结构体或类来表示链表节点,这样可以使代码更加清晰,并减少错误的可能性。
    • 在这个结构体或类中,可以包含节点的值、左指针和右指针。
  3. 封装链表操作
    • 将链表的操作(如插入、删除、遍历等)封装到类的方法中,这样可以使代码更加模块化。
    • 封装后的代码可以提供更好的封装性、继承性和多态性。
  4. 使用迭代器或指针
    • 考虑使用迭代器或指针来遍历链表,而不是直接使用数组索引。这可以使代码更加灵活,并减少错误的可能性。
  5. 改进输入验证
    • main函数中,添加对输入数据的验证,确保输入的操作码和参数是有效的。
    • 可以使用std::cin.fail()或类似的函数来检查输入是否成功。
  6. 使用标准库容器
    • 如果可能的话,考虑使用C++标准库中的容器(如std::list)来实现链表。这样可以减少手动管理内存和指针的复杂性,并提高代码的可读性和可维护性。
  7. 改进遍历打印逻辑
    • 遍历链表时,可以使用循环而不是递归,以提高效率。
    • 在打印链表时,可以添加一些分隔符或换行符,使输出更加清晰。
  8. 优化内存使用
    • 如果链表中的节点数量非常大,可以考虑使用动态内存分配来减少内存使用。
    • 但是,请注意,动态内存分配也会增加内存泄漏和指针错误的风险。
  9. 添加注释和文档:为代码添加注释,解释每个函数、类和变量的作用。编写文档,描述如何使用这个链表类,以及它的特性和限制。
  10. 使用异常处理:如果在链表操作中发生错误(如无效索引、无效操作等),可以使用C++的异常处理机制来抛出异常,并在调用者处捕获和处理这些异常。这可以使错误处理更加清晰和一致。

这篇关于双链表——AcWing.827双链表的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1066704

相关文章

数据结构——双链表实现和注释浅解

关于双链表的基础部分增删查改的实现和一点理解,写在注释里~  前言              浅记   1. 哨兵位的节点不能被删除,节点的地址也不能发生改变,所以是传一级指针 2. 哨兵位并不存储有效数据,所以它并不是有效节点 3. 双向链表为空时,说明只剩下一个头节点(哨兵位)  List.h #pragma once#include<

浅谈单链表与双链表的区别

数组的优点 随机访问性强(通过下标进行快速定位) 查找速度快 数组的缺点 插入和删除效率低(插入和删除需要移动数据) 可能浪费内存(因为是连续的,所以每次申请数组之前必须规定数组的大小,如果大小不合理,则可能会浪费内存) 内存空间要求高,必须有足够的连续内存空间。 数组大小固定,不能动态拓展 链表的优点 插入删除速度快(因为有next指针指向其下一个节点,通过改变指针的指向可以方便的增加删除元素)

数据结构-双链表-详解

数据结构-双链表-详解 1.前言2.结构2.1双向2.2带头2.3循环 3.实现3.1结构体3.2初始化与删除初始化删除 3.3插入尾插头插 3.4删除尾删头删 3.4查找3.5pos位置的插入删除 1.前言 链表总共有八种:双向、单向;带头、不带头;循环、不循环。(8 = 2 * 2 * 2) 在前两篇博客中,我讲了单链表,具体来说,是 单向、不带头、不循环 的链表。 这种

2024.8.24 Python,链表异常断裂问题,双链表的建立问题,全排列中的引用机制与copy的使用,最大子数组和

1.浏览器系统设计 你有一个只支持单个标签页的 浏览器 ,最开始你浏览的网页是 homepage ,你可以访问其他的网站 url ,也可以在浏览历史中后退 steps 步或前进 steps 步。 请你实现 BrowserHistory 类: BrowserHistory(string homepage) ,用 homepage 初始化浏览器类。 void visit(string url)

链表初解(二)——双链表的创建、删除、插入

下面是基本的双链表操作,由于双链表有两个方向,所以在删除和插入节点时,可以节省一个指针,只用一个链表上的指针和一个待操作的指针即可完成插入和删除;同时也要注意在编写双链表时对情况的判断要仔细,否则很容易出错~ #include<iostream>using namespace std;typedef struct student{int data;struct student *next

使用单指针实现双链表(C++语言)

本文是Clifford A. Shaffer所著《数据结构与算法分析》(C++版)习题4.4的解答。 链表是常见的数据结构,链表中的结点通常定义如下。 template <typename E> class Link {public:E element;Link<E> *next;Link(const E& it, Link<E>* next) { }}; 结点是一个定义为Link的模

在单链表和双链表中删除倒数第k个节点

实现的完整代码如下: //在单链表和双链表中删除倒数第k个节点public class DeleteList{//单链表节点的定义public static class Node{int value;Node next;public Node(int data){this.value=data;}}//删除单链表中倒数第k个节点public static int DeletList_K(

Blossom:支持私有部署的云端双链笔记软件分享

Blossom 是一款支持私有部署的云端双链笔记软件,能够帮助用户将笔记、图片和个人计划安排保存在自己的服务器中,并在任意设备之间实时同步。同时,它还可以作为一个动态博客使用。本文将详细介绍 Blossom 的特点和使用方法。 一、Blossom 的特点 1. 完善的文件关系 Blossom 不依赖任何三方存储和图床,其本身就是一个图床。它提供了完善的图片管理功能,包括防误删和图片与文章

数组模拟单链表和双链表

目录 单链表 初始化 头插 删除 插入  双链表 初始化 插入右和插入左 删除 单链表 单链表主要有三个接口:头插,删除,插入(由于单链表的性质,插入接口是在结点后面插入) 初始化 int e[N], ne[N]; // 不使用next[N],为和库中next分开,以免命名冲突int index, head;void init(){head = -1;

【数据结构初阶】--- 双链表

双链表你要了解的 双链表: 带头双链表循环双链表带头循环双链表 今天我们学习的,就是最强双链表 — 带头循环双链表 下图就是带头循环双链表,所谓带头,就是有哨兵位,那么最左边的节点就是哨兵位,是不计入链表的节点数,并且不存储数据,那用来做什么呢?同学们可以提前猜想它的作用,后面我会解答的 带头循环双链表的特点 从三个维度分析: 带头:所谓带头就是有哨兵位, 先试想,在学单链表的时候,