算法刷题笔记 双链表(C++实现)

news/2024/7/7 20:01:23 标签: 算法, 笔记, c++

文章目录

    • 题目描述
    • 基本思路
    • 实现代码

题目描述

  • 实现一个双链表,双链表初始为空,支持5种操作:

    • 在最左侧插入一个数;
    • 在最右侧插入一个数;
    • 将第k个插入的数删除;
    • 在第k个插入的数左侧插入一个数;
    • 在第k个插入的数右侧插入一个数
  • 现在要对该链表进行M次操作,进行完所有操作后,从左到右输出整个链表。

  • 注意:题目中第k个插入的数并不是指当前链表的第k个数。例如操作过程中一共插入了n个数,则按照插入的时间顺序,这n个数依次为:第1个插入的数,第2个插入的数,…第n个插入的数。

输入格式

  • 第一行包含整数M,表示操作次数。
  • 接下来M行,每行包含一个操作命令,操作命令可能为以下几种:
    • L x,表示在链表的最左端插入数x
    • R x,表示在链表的最右端插入数x
    • D k,表示将第k个插入的数删除。
    • IL k x,表示在第k个插入的数左侧插入一个数。
    • IR k x,表示在第k个插入的数右侧插入一个数。

输出格式

  • 共一行,将整个链表从左到右输出。

数据范围

  • 1 ≤ M ≤ 100000
  • 所有操作保证合法。

基本思路

  • 双链表实际上就是对单链表的衍生。单链表中,每一个结点只需要存储自己的下一个结点的位置(通过下标或指针的方式)即可,但是双链表中的结点需要存储自己的上一个结点的位置。另外,双链表中往往不只有头指针,还有尾指针。
  • 和单链表的实现思路类似,出于效率考虑,我们并不会在每次新建一个链表结点时都使用C++中的new运算符创建一个新结点,而是开辟一个空间足够大的静态数组,来模拟单链表。链表中每一个结点存储的左右两个结点的位置也分别通过一个静态数组进行表示。本题的最大难点就是对于每一种操作,都需要考虑是否要分情况,是否要合并多种情况。

实现代码

#include <iostream>
using namespace std;

const int N = 1e5 + 10;
int list[N];
int l[N], r[N];
int head = -1, tail = -1, current = 0;

inline void insert_left(void)
{
    // 输入结点信息
    int x;
    cin >> x;
    // 创建一个新结点
    list[current] = x;
    l[current] = -1;
    r[current] = head;
    // 修改原先的首结点
    if(head != -1) l[head] = current;
    else tail = current;
    // 将新插入的结点设置为首结点,同时修改当前位置
    head = current;
    current ++;
}

inline void insert_right(void)
{
    // 输入结点信息
    int x;
    cin >> x;
    // 创建一个新结点
    list[current] = x;
    l[current] = tail;
    r[current] = -1;
    // 修改原先的尾结点
    if(tail != -1) r[tail] = current;
    else head = current;
    // 将新插入的结点设置为尾结点,同时修改当前位置
    tail = current;
    current ++;
}

inline void delete_k(void)
{
    //输入部分
    int k;
    cin >> k;
    // 第一种情况,即该结点不是首结点也不是尾结点
    if(head != k - 1 && tail != k - 1)
    {
        // 找出该结点的前一个结点和后一个结点的下标
        int before = l[k - 1], after = r[k - 1];
        // 修改前一个结点的后一个元素下标和后一个结点的前一个元素下标
        r[before] = r[k - 1];
        l[after] = l[k - 1];
    }
    // 第二种情况,即该结点是首结点但是不是尾结点
    else if(head == k - 1 && tail != k - 1) 
    {
        head = r[k - 1];
        l[head] = -1;
    }
    // 第三种情况,即该结点是尾结点但是不是首结点
    else if(tail == k - 1 && head != k - 1)
    {
        tail = l[k - 1];
        r[tail] = -1;
    }
    // 第四种情况,该结点是链表中唯一结点
    else
    {
        head = -1;
        tail = -1;
    }
}

inline void insert_before_k(void)
{
    int k;
    cin >> k;
    if(head == k - 1) insert_left();
    else
    {
        int x;
        cin >> x;
        // 找到第k个结点的前一个结点
        int before = l[k - 1];
        // 创建一个新的结点
        list[current] = x;
        l[current] = before;
        r[current] = k - 1;
        // 修改前一个结点的后指针
        r[before] = current;
        // 修改第k个结点的前指针
        l[k - 1] = current;
        // 修改当前指针
        current ++;
    }
}

inline void insert_after_k(void)
{
    int k;
    cin >> k;
    if(tail == k - 1) insert_right();
    else
    {
        int x;
        cin >> x;
        // 找到第k个结点的后一个结点
        int after = r[k - 1];
        // 创建一个新的结点
        list[current] = x;
        l[current] = k - 1;
        r[current] = after;
        // 修改第k个结点的后指针
        r[k - 1] = current;
        // 修改后一个结点的前指针
        l[after] = current;
        // 修改当前指针
        current ++;
    }
}

int main(void)
{
    // 输入操作次数
    int M;
    cin >> M;
    // 输入每一次的操作,并分情况讨论
    for(int i = 0; i < M; ++i)
    {
        string operation;
        cin >> operation;
        if(operation == "L") insert_left();
        else if(operation == "R") insert_right();
        else if(operation == "D") delete_k();
        else if(operation == "IL") insert_before_k();
        else if(operation == "IR") insert_after_k();
    }
    // 循环输出整个链表
    while(head != -1)
    {
        cout << list[head] << " ";
        head = r[head];
    }
    return 0;
}

http://www.niftyadmin.cn/n/5535130.html

相关文章

嵌入式软件stm32面试

一、STM32的内核型号有哪些? STM32系列是STMicroelectronics(意法半导体)生产的基于ARM Cortex-M内核的微控制器产品线。这些产品按照不同的内核架构和性能特点分为了主流产品、超低功耗产品和高性能产品。 1.1 主流产品 STM32F0 系列:搭载 ARM Cortex-M0 内核。STM32F1 …

Java实现单点登录(SSO)详解:从理论到实践

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; ✨✨ 帅哥美女们&#xff0c;我们共同加油&#xff01;一起进步&am…

leetcode216.组合总和III、40.组合总和II、39.组合总和

216.组合总和III 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。 示例 1: 输入: k 3, n 7 输出…

vue3中使用Antv G6渲染树形结构并支持节点增删改

写在前面 在一些管理系统中&#xff0c;会对组织架构、级联数据等做一些管理&#xff0c;你会怎么实现呢&#xff1f;在经过调研很多插件之后决定使用 Antv G6 实现&#xff0c;文档也比较清晰&#xff0c;看看怎么实现吧&#xff0c;先来看看效果图。点击在线体验 实现的功能…

Spring Cloud Alibaba之负载均衡组件Ribbon

一、什么是负载均衡&#xff1f; &#xff08;1&#xff09;概念&#xff1a; 在基于微服务架构开发的系统里&#xff0c;为了能够提升系统应对高并发的能力&#xff0c;开发人员通常会把具有相同业务功能的模块同时部署到多台的服务器中&#xff0c;并把访问业务功能的请求均…

如何通过IP地址查询地理位置及运营商信息

在数字时代&#xff0c;IP地址&#xff08;Internet Protocol Address&#xff0c;互联网协议地址&#xff09;已经成为我们日常网络活动的重要组成部分。每台连接到互联网的设备都被分配了一个唯一的IP地址&#xff0c;它不仅可以识别设备&#xff0c;还可以揭示设备的地理位置…

Perl 语言开发(三):运算符和表达式

目录 1. 算术运算符 1.1 基本算术运算符 1.2 自增和自减运算符 2. 字符串运算符 2.1 连接运算符 2.2 重复运算符 3. 赋值运算符 3.1 简单赋值运算符 3.2 复合赋值运算符 4. 比较运算符 4.1 数字比较运算符 4.2 字符串比较运算符 5. 逻辑运算符 5.1 逻辑运算符 5…

HQ-SAM

不建议复现