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

手写链表C++

目录

一、链表基本概念以及注意事项

1.1 构造函数与析构函数

1.2 插入元素

1.3 重载运算符

二、小结


一、链表基本概念以及注意事项

        在工作中,链表是一种常见的数据结构,可以用于解决很多实际问题。在学习中,掌握链表可以提高编程能力和算法思维能力。在面试中,手写链表是一个常考的知识点,能够考察应聘者的编程水平和代码实现能力。因此,掌握手写C++链表对于程序员来说是非常重要的。

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

        使用纯C/C++实现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 <- 4Node* 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;
}

1.1 构造函数与析构函数

链表由多个Node节点组成 ,每个节点由数据和指针构成。其中,指针是下一个连接节点的地址。其中Node初始化数据一定要做,如下。

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

List的构造函数实现,这里给头节点申请内存,size_参数初始化为0,后续每插入一个元素,就+1。

List()
{this->head_ = new Node();// 分配空间this->size_ = 0;
};

整个链表,实现任何功能,我们都要维护一个头节点和size_。析构函数如下,必须是串行的方式析构每一个节点,不然可能造成内存泄漏。

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;
}

在保证不断链的前提下;释放整个链表,似乎只能从后面开始delete。循环当中,每次遍历到 待删除节点前一个p_curr, 然后delete p_curr->next_

1.2 插入元素

//在第pos个元素前一个位置插入(创建、找到位置、入链表)
void List::insert(int pos, int value)
{if (pos < 0 || pos > size_)return;//创建新的节点接受数据Node* newnode = new Node();newnode->data_ = value;Node* p_curr = head_;for (int i = 0; i < pos; i++){p_curr = p_curr->next_;}//新节点入链表newnode->next_ = p_curr->next_;p_curr->next_ = newnode;size_++;
}

18行、19行代码很好诠释了插入过程;其中18行代码:将当前节点的指针域指向 对象的地址 赋给 newnode->next_;保证了新数据与链表中后面连接;

19行代码:当前节点指针域指向 新的节点。例如: 1 2 3 4 5  要在 4 和 5 之间插入 一个 666,步骤如下:

  • 遍历至节点4;    1 2 3 4 5
  • 将666 指向5:; 1 2 3 4      666 5
  • 再将 4 指向 666; 1 2 3 4 666 5

1.3 重载运算符

C++ STL中的List是不能通过[ ]来访问元素的,只有vector可以。本文这里给自定义的List重载一个 [ ],实现类似:arr[2]来访问元素的方法。

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_;
}

二、小结

        本文我们将实现最基础的List。List是一个常见的数据结构,用于存储有序的元素集合。在C++中,我们可以使用类和指针来实现List。

        下一篇文章将带来《C++ 实现List的反转、删除、合并》。这些操作在面试中经常被问到,因为它们是List的基本操作之一。通过反转、删除和合并List,我们可以更加深入地了解List的实现方式和应用场景。同时,这些操作也是许多算法的基础,例如排序、查找等。因此,掌握这些操作对于面试和实际应用都非常重要。

        在接下来的文章中,我们将介绍如何实现List的反转、删除和合并。如果您对这些操作感兴趣,敬请关注我们的下一篇文章!

相关文章:

  • Hadoop学习总结(使用Java API操作HDFS)
  • [工业自动化-10]:西门子S7-15xxx编程 - PLC主站 - 信号量:数字量
  • C语言如何执行HTTP GET请求
  • linux espeak语音tts;pyttsx3 ubuntu使用
  • Linux系统编程——文件的光标移动
  • 前端设计模式之【访问者模式】
  • 计算机视觉与深度学习 | 改进的SIFT立体匹配算法
  • IP行业API助力于网络分析和数据挖掘
  • centos安装docker和docker-compose
  • 华为eNSP实验-QinQ基本实验
  • 【OpenHarmony内核】Harmony内核之线程操作函数(二)
  • sql语句-实体属性有集合怎么批量查询
  • react 修改less文件后保存,内存溢出,项目崩溃问题解决
  • 解锁潜在商机的钥匙——客户管理系统公海池
  • maven打包可运行jar
  • $translatePartialLoader加载失败及解决方式
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • android图片蒙层
  • Java 网络编程(2):UDP 的使用
  • Javascript Math对象和Date对象常用方法详解
  • React系列之 Redux 架构模式
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • WinRAR存在严重的安全漏洞影响5亿用户
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 对象管理器(defineProperty)学习笔记
  • 分享几个不错的工具
  • 高性能JavaScript阅读简记(三)
  • 关于 Cirru Editor 存储格式
  • 区块链分支循环
  • 数组的操作
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • ​Spring Boot 分片上传文件
  • ​TypeScript都不会用,也敢说会前端?
  • #{}和${}的区别是什么 -- java面试
  • #在 README.md 中生成项目目录结构
  • (04)odoo视图操作
  • (1) caustics\
  • (4)事件处理——(2)在页面加载的时候执行任务(Performing tasks on page load)...
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (三)模仿学习-Action数据的模仿
  • (详细版)Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models
  • (一)spring cloud微服务分布式云架构 - Spring Cloud简介
  • (原)Matlab的svmtrain和svmclassify
  • (原創) 如何刪除Windows Live Writer留在本機的文章? (Web) (Windows Live Writer)
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • (轉貼) UML中文FAQ (OO) (UML)
  • * 论文笔记 【Wide Deep Learning for Recommender Systems】
  • ... fatal error LINK1120:1个无法解析的外部命令 的解决办法
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .NET Core中的去虚
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...