手写C++ 实现链表的反转、删除、合并

news2024/11/19 16:22:52

目录

一、手写List成员方法

1.1 打印链表

1.2 删除链表节点

1.3 链表中倒数第k个节点

1.4 反转链表

1.5 合并两个排序链表

二、完整代码


 

一、C++实现链表成员方法

        在上一篇博客《手写链表C++》,实现了基本的List类。在面试中,经常被问到List如何反转、删除元素等,同时也为了丰富List类的成员;这一节本文实现如题等list操作。

        C++链表,一种重要的数据结构,由一系列节点构成,每个节点包含两部分:数据和指向下一个节点的指针。链表是一种物理存储单元上非连续、非顺序的存储结构,数据结构的逻辑顺序是通过链表中的指针链接次序实现的。链表的最简单形式是单向链表,它只包含一个信息域和一个指针域。链表的优点是可以动态地分配内存空间,实现高效的数据操作。在C++中,链表的每个节点都是通过指针链接在一起,从而形成一个连续的链式结构。

1.1 打印链表

为了方便debug代码,先写一个打印链表的函数:

void List::print()
{
    if (size_ == 0)
    {
        cout << "size = 0" << endl;
        return;
    }
    //遍历
    Node* p_curr = head_->next_;//【注意这里next】
    while (p_curr != nullptr)
    {
        cout << p_curr->data_ << " ";
        p_curr = p_curr->next_;
    }
    cout << endl;
}

1.2 删除链表节点

思想就是找到要删除的Node的前一个节点,让前一个节点的指针指向Node的下一个节点就行了。

例如:pos = 3的时候,for循环执行完毕,p_curr表示索引值为2的节点地址,接着我们让p_curr->next 指向 下一个节点的下一个节点。

//功能:删除索引位置为pos的节点
void List::remove(int pos)
{
    if (pos < 0 || pos > size_)
    {
        return;
    }
    Node* p_curr = head_;
    for (int i = 0; i < pos; i++)// 3
    {
        p_curr = p_curr->next_;
    }
    p_curr->next_ = p_curr->next_->next_;
    size_--;
}

1.3 链表中倒数第k个节点

你可以去看看剑指offer上的做法,我这里类List维护了一个size_,所以比较简单。

//链表中倒数第k个节点
int List::get_reverse_element(int reverse_pos)
{
    int pos = size_ - reverse_pos;
    Node* p_curr = head_;
    for (int i = 0; i < pos; i++)
    {
        p_curr = p_curr->next_;
    }
    return p_curr->data_;
}

1.4 反转链表

这个方法是必学的,因为面试中经常问,甚至需要现场手撕。

//反转链表
void List::reverse()
{
    // head   -> 1 -> 2 -> 3 -> 4 -> nullptr
    //nullptr <- 1 <- 2 <- 3 <- 4

    Node* p_curr = head_->next_;
    Node* p_prev = nullptr;
    while (p_curr != nullptr)
    {
        Node* p_next = p_curr->next_;
        if (p_next == nullptr)
        {
            head_->next_ = p_curr;
        }
        p_curr->next_ = p_prev;
        p_prev = p_curr;
        p_curr = p_next;
    }
}

下图是反转效果:

ec73148a7a4b46bf85b735c511905637.png

1.5 合并两个排序链表

//合并两个排序链表
void mergeLists(List& list3, List& list4, List& list34)
{
    Node* p_curr3 = list3.head_->next_;
    Node* p_curr4 = list4.head_->next_;
    Node* p_curr34 = list34.head_->next_;
    int location = 0;
    while ((p_curr3 != nullptr) || (p_curr4 != nullptr))
    {
        if ((p_curr3 != nullptr) && (p_curr4 != nullptr))
        {
            if (p_curr3->data_ < p_curr4->data_)
            {
                list34.insert(location, p_curr3->data_);
                location++;
                list34.insert(location, p_curr4->data_);
                location++;
            }
            else
            {
                list34.insert(location, p_curr4->data_);
                location++;
                list34.insert(location, p_curr3->data_);
                location++;
            }
            p_curr3 = p_curr3->next_;
            p_curr4 = p_curr4->next_;
        }
        else if ((p_curr3 != nullptr) && (p_curr4 == nullptr))
        {
            list34.insert(location, p_curr3->data_);
            location++;
            p_curr3 = p_curr3->next_;
        }
        else if ((p_curr3 == nullptr) && (p_curr4 != nullptr))
        {
            list34.insert(location, p_curr4->data_);
            location++;
            p_curr4 = p_curr4->next_;
        }
    }
}

例如现在有两个升序序列:

  • A:0 2 4 6 8 14                                                                                                       
  • B:1 3 5 7 9 12 21 31 

要将他们变成一个生序序列;

思路:假设现在两个序列元素个数相等;我们将 0 1对比将得到 0 1, 再将1和2 3 对比 得到 0 1 2 3;再将3和4 5 对比;依次类推,(对应第10行代码)

现在考虑 A序列长度 > B序列长度;对应第29行代码; A序列长度 < B序列长度;对应上述第35行代码。

//合并两个排序链表
    List list3,list4;
    for (int i = 0; i < 5; i++)
    {
        list3.insert(i, 2*i);
        list4.insert(i, 2 * i + 1);
    }
    list3.insert(5, 14);
    list4.insert(5, 12);
    list4.insert(6, 21);
    list4.insert(7, 31);
    list3.print();
    list4.print();

    List list34;
    mergeLists(list3, list4, list34);
    list34.print();

测试用例:

216e44bc448d4c09785c6037589f395b.jpeg

二、链表完整代码

以下代码包含:List的实现,节点定义,链表的插入、删除、合并、反转等。

#include<iostream>
using namespace std;

class Node
{
public:
    int data_;//数据阈
    Node* next_;//指针阈
public:
    Node() :data_(-1), next_(nullptr) {}
};

class List
{
public:
    List()
    {
        this->head_ = new Node();// 不分配空间,下面赋值是不合理的!
                                 //this->head_->data_ = 0;//多余?
        this->head_->next_ = nullptr;
        this->size_ = 0;
    };
    void insert(int pos, int value);
    void remove(int pos);
    int get_reverse_element(int reverse_pos);//链表中倒数第k个节点
    void reverse();

    int operator[](int i);
    void print();
    ~List();
public:
    Node* head_;
    int size_;//维护一个size
};
//在第pos个元素前一个位置插入(创建、找到位置、入链表)
void List::insert(int pos, int value)
{
    if (pos < 0 || pos > size_)
        return;

    //创建新的节点接受数据
    Node* newnode = new Node();
    newnode->data_ = value;
    //cout << "newnode->data_ = " << *newnode->data_ << endl;
    newnode->next_ = nullptr;

    //利用辅助指针找到pos前一个节点
    // 其实这里不断next,无非就是希望p_curr = nullptr
    // 然后56行 让newnode->next_  = nullptr(这个nullptr是从head_->next 传过来的);也就是尾部插入嘛
    // 而循环链表 同理 让newnode->next_  = &(head_)(这个 &(head_) 是从head_->next 传过来的);
    Node* p_curr = head_;
    for (int i = 0; i < pos; i++) //这个for循环本质上是head_->next_->next_......
    {
        p_curr = p_curr->next_;
    }
    //现在p_curr就是pos前一个节点的指针阈
    //新节点入链表
    newnode->next_ = p_curr->next_;//右边
    p_curr->next_ = newnode;//左边
    size_++;
}

void List::remove(int pos)
{
    if (pos < 0 || pos > size_)
    {
        return;
    }
    Node* p_curr = head_;
    for (int i = 0; i < pos; i++)// 3
    {
        p_curr = p_curr->next_;
    }
    p_curr->next_ = p_curr->next_->next_;
    size_--;
}

//链表中倒数第k个节点
int List::get_reverse_element(int reverse_pos)
{
    int pos = size_ - reverse_pos;
    Node* p_curr = head_;
    for (int i = 0; i < pos; i++)
    {
        p_curr = p_curr->next_;
    }
    return p_curr->data_;
}

//反转链表
void List::reverse()
{
    // head   -> 1 -> 2 -> 3 -> 4 -> nullptr
    //nullptr <- 1 <- 2 <- 3 <- 4

    Node* p_curr = head_->next_;
    Node* p_prev = nullptr;
    while (p_curr != nullptr)
    {
        Node* p_next = p_curr->next_;
        if (p_next == nullptr)
            if (p_curr->next_ == nullptr)
            {
                head_->next_ = p_curr;
            }
        p_curr->next_ = p_prev;
        p_prev = p_curr;
        p_curr = p_next;
    }
}

int List::operator[](int i)
{
    Node* p_curr = head_;
    int count = 0;
    while (count <= i)
    {
        p_curr = p_curr->next_;
        count++;
    }
    return p_curr->data_;
}
void List::print()
{
    if (size_ == 0)
    {
        cout << "size = 0" << endl;
        return;
    }
    //遍历
    Node* p_curr = head_->next_;//【注意这里next】
    while (p_curr != nullptr)
    {
        cout << p_curr->data_ << " ";
        p_curr = p_curr->next_;
    }
    cout << endl;
}
List::~List()
{
    while (size_ != 0)
    {
        Node* p_curr = head_;
        for (int i = 0; i < (size_ - 1); i++)// 012345 i < 5
        {
            p_curr = p_curr->next_;//for循环执行完,p_curr指向4
        }
        delete p_curr->next_;//删除最后一个元素
        p_curr->next_ = nullptr;//末尾元素 空指针
        size_--;
        print();
    }
    delete head_; //【这个容易忘记!】
    cout << "delete!" << endl;
}

//合并两个排序链表
void mergeLists(List& list3, List& list4, List& list34)
{
    Node* p_curr3 = list3.head_->next_;
    Node* p_curr4 = list4.head_->next_;
    Node* p_curr34 = list34.head_->next_;
    int location = 0;
    while ((p_curr3 != nullptr) || (p_curr4 != nullptr))
    {
        if ((p_curr3 != nullptr) && (p_curr4 != nullptr))
        {
            if (p_curr3->data_ < p_curr4->data_)
            {
                list34.insert(location, p_curr3->data_);
                location++;
                list34.insert(location, p_curr4->data_);
                location++;
            }
            else
            {
                list34.insert(location, p_curr4->data_);
                location++;
                list34.insert(location, p_curr3->data_);
                location++;
            }
            p_curr3 = p_curr3->next_;
            p_curr4 = p_curr4->next_;
        }
        else if ((p_curr3 != nullptr) && (p_curr4 == nullptr))
        {
            list34.insert(location, p_curr3->data_);
            location++;
            p_curr3 = p_curr3->next_;
        }
        else if ((p_curr3 == nullptr) && (p_curr4 != nullptr))
        {
            list34.insert(location, p_curr4->data_);
            location++;
            p_curr4 = p_curr4->next_;
        }
    }
}

int main()
{
    List list1;
    //插入
    for (int i = 0; i < 15; i++)
    {
        list1.insert(i, i);
    }
    //删除
    list1.remove(10);
    list1.remove(5);
    //打印
    list1.print();
    list1.reverse();
    list1.print();
    //访问倒数元素
    for (int i = 1; i < 4; i++)
    {
        cout << "倒数第" << i << "个元素是:" << list1.get_reverse_element(i) << endl;
    }
    list1.insert(2, 9999);
    //重载符[]
    for (int i = list1.size_ - 1; i >= 0; i--)
    {
        cout << list1[i] << " ";
    }
    cout << endl;
    List list2;
    list2.insert(0, 10);
    list2.insert(1, 20);
    list2.insert(2, 30);
    list2.print();
    int size2 = list2.size_;
    //合并两个排序链表
    List list3, list4;
    for (int i = 0; i < 5; i++)
    {
        list3.insert(i, 2 * i);
        list4.insert(i, 2 * i + 1);
    }
    list4.insert(5, 12);
    list4.insert(6, 21);
    list3.print();
    list4.print();
    List list34;
    mergeLists(list3, list4, list34);
    list34.print();
    return 1;
}

 

 

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

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

相关文章

代码随想录训练营Day2:1.有序数组的平方 2.长度最小的子数组3,螺旋矩阵

本专栏内容为&#xff1a;代码随想录训练营学习专栏&#xff0c;用于记录训练营的学习经验分享与总结。 文档讲解&#xff1a;代码随想录 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的代码仓…

基于SSM的超市库存商品管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

dbeaver连接别人的数据库没有表

1.概念 非缺省的数据库&#xff1a; 通常是指在一个数据库管理系统&#xff08;DBMS&#xff09;中&#xff0c;除了系统默认创建的数据库之外的其他用户创建或自定义的数据库。许多数据库系统在安装后会创建一个默认数据库&#xff0c;例如MySQL中的mysql数据库&#xff0c;…

【lib.dll.a.so】Windows和Linux两个系统下的库文件

1.静态库&&动态库 Windows平台下&#xff1a;静态库后缀为.lib&#xff0c;动态库后缀为.dll Linux平台下&#xff1a;静态库格式为lib**.a&#xff0c;动态库格式为lib**.so 谈论两者区别之前&#xff0c;需要对程序编译和运行有一个大致认识&#xff1a; 代码想要…

765. 情侣牵手(困难)

首先不考虑已经正确坐在一起的组合在没有坐在一起的组合中&#xff0c;只有当两对情侣互相配对时只需要一次交换操作就可以使得两对情侣完成匹配&#xff0c;其余情况交换数等于情侣对数可以把所有情侣看成一个大集合&#xff0c;这个大集合是可以拆成若干小集合的&#xff0c;…

QGIS导出Geoserver样式加载

1.在QGIS中加载并设计样式 加载数据之后按F7键即可打开样式编辑器 可以右键图层&#xff0c;点击属性中的符号化&#xff0c;有一个“基于规则”&#xff0c;可以设定规则或者比例尺范围。可以实现一定比例尺缩放可见或不可见的效果。 2.设计完样式之后右键图层导出 选择保…

欧拉角(横滚角、俯仰角、偏航角)、旋转矩阵、四元数的转换与解决万向节死锁

1、概述 物体的位姿&#xff08;位置和方向&#xff09;的描述方法一般使用两个坐标系来表示&#xff0c;一个是世界坐标系或地面坐标系&#xff0c;这里我都叫做地面坐标系吧&#xff0c;属于参考坐标系&#xff1b;另一个是自身的坐标系&#xff0c;以飞机为例来讲述一些常见…

Linux学习教程(第一章 简介)4

第一章 简介 十一、Linux的主要应用领域有哪些? Linux 似乎在我们平时的生活中很少看到,那么它应用在哪些领域呢?其实,在生活中随时随地都有 Linux 为我们服务着。 1、网站服务器 用事实说话!访问国际知名的 Netcraft 网站 http:// www.netcraft.com,在 "Whats …

NLP领域的突破催生大模型范式的形成与发展

当前的大模型领域的发展&#xff0c;只是范式转变的开始&#xff0c;基础大模型才刚刚开始改变人工智能系统在世界上的构建和部署方式。 1、大模型范式 1.1 传统思路&#xff08;2019年以前&#xff09; NLP领域历来专注于为具有挑战性的语言任务定义和设计系统&#xff0c…

OpenCV C++ 图像处理实战 ——《多二维码识别》

OpenCV C++ 图像处理实战 ——《多二维码识别》 一、结果演示二、zxing库配置2.1下载编译三、多二维码识别3.1 Method one3.1.1 源码3.2 Method two3.2.1 源码四、源码测试图像下载总结一、结果演示 </

王道数据结构课后代码题p150 15.设有一棵满二叉树(所有结点值均不同),已知其先序序列为 pre,设计一个算法求其后序序列post。(c语言代码实现)

对一般二叉树&#xff0c;仅根据先序或后序序列&#xff0c;不能确定另一个遍历序列。但对满二叉树&#xff0c;任意一个结点的左、右子树均含有相等的结点数&#xff0c;同时&#xff0c;先序序列的第一个结点作为后序序列的最后个结点。 本题代码如下 void pretopost(char …

目标检测最新创新点: EMS-YOLO:首个用于目标检测的直接训练脉冲神经网络

EMS-YOLO&#xff1a;第一个用于目标检测的深度直接训练脉冲神经网络&#xff0c;首次使用代理梯度训练深度 SNN 进行检测&#xff0c;并设计全脉冲残差块EMS-ResNet&#xff0c;代码刚刚开源&#xff01;单位&#xff1a;国科大, 西安交大, 清华, 北大, 华为 脉冲神经网络 (S…

【MybatisPlus】条件构造器、自定义SQL、Service接口

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 Redis 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 MybatisPlus 一、条件构造器1.1 基于QueryW…

使用 huggingface_hub 镜像下载 大模型

download.py &#x1f447; import os # 配置 hf镜像 os.environ[HF_ENDPOINT] https://hf-mirror.com# 设置保存的路径 local_dir "XXXXXX"# 设置仓库id model_id "sensenova/piccolo-large-zh"cmd f"huggingface-cli download --resume-downlo…

Android---MVP 中 presenter 声明周期的管理

我们经常在 Android MVP 架构中的 Presenter 层做一些耗时操作&#xff0c;比如请求网络数据&#xff0c;然后根据请求后的结果刷新 View。但是&#xff0c;如果按返回结束 Activity&#xff0c;而 Presenter 依然在执行耗时操作。那么就有可能造成内存泄漏&#xff0c;严重时甚…

Pytorch常用的函数(四)深度学习中常见的上采样方法总结

Pytorch常用的函数(四)深度学习中常见的上采样方法总结 我们知道在深度学习中下采样的方式比较常用的有两种&#xff1a; 池化 步长为2的卷积 而在上采样过程中常用的方式有三种&#xff1a; 插值 反池化 反卷积 不论是语义分割、目标检测还是三维重建等模型&#xff0…

使用迁移学习在线校准深度学习模型

使用迁移学习在线校准深度学习模型 本文参考的是2023年发表于Engineering Applications of Artificial Intelligence, EAAI的Deep Gaussian mixture adaptive network for robust soft sensor modeling with a closed-loop calibration mechanism 1. 动机 概念漂移导致历史训…

基于SSM的科技公司门户网站

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

幸运素数(找出给定区间的所有幸运素数)

从键盘输入一个区间&#xff0c;程序判定输出区间的所有幸运素数。 (笔记模板由python脚本于2023年11月11日 12:44:43创建&#xff0c;本篇笔记适合熟悉python整型数据类型和基本编程技巧的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.o…

如何将NetCore Web程序独立发布部署到Linux服务器

简介 在将 .NET Core 应用程序部署到 Linux 服务器上时,可以采用独立发布的方式,以便在目标服务器上运行应用程序而无需安装 .NET Core 运行时。本文介绍如果将NetCore Web程序独立发布部署到Linux服务器。 1、准备一台服务器 服务器配置:2核2G 系统环境:Alibaba Cloud…