前言

链表是一种非常非常基础的数据结构,本文首先讲解链表的基础知识,然后使用C++的模板实现了一个链表类,并简单实现了常见的插入、删除、查找等算法。

阅读本文需要对C/C++的指针具有一定的了解。

基础知识

链表是一种逻辑上连续,内存上分散的线性表数据结构,其基本单位为结点,每个结点分数据区和指针区,数据区用于存放数据,指针区则用于指向其他结点,通过指针每个结点就被串接成了一条“链子”。

单链表

最基本的单链表结构如下图所示:

单链表每个结点包含一个指针,该指针指向下一个结点,最后一个结点的指针则为NULL,通常也会通过NULL判断是否到达链表的尾部。因此,单链表无法“回头”,只能向前遍历,不能向后遍历。假设需要访问结点D的数据,就需要将头指针head赋值给一个指针p,然后让p依次前移两次,此时p指向D结点,就可以通过指针p访问D结点的数据。所谓前移即如下操作:

p = p->next

插入操作

假如需要在C结点后面插入F结点,只需要使C结点的指针指向F,F结点的指针指向结点D即可,如图所示

需要注意的是,在具体实现的时候,需要先暂存C结点的指针,先将其赋值给F结点指针,然后再将F结点的地址赋值给C结点的指针,否则会丢失D结点的地址,链表就会在此处断开。

删除操作

假如需要删除D结点,则直接让C结点的指针指向E结点即可,如图所示

以上图片素材来源于《代码随想录:链表理论基础》,原文链接,侵删。

与数组的对比

相比于数组,链表存在如下优势:

  • 不要求连续的储存空间,且链表的长短随时可变,空间利用率高
  • 因为链表的线性特征是利用指针实现的,所以链表的插入和删除都非常方便,只需要修改节点的指针即可,不需要像数组一样需要挪动元素的位置。

当然,天下没有白吃的午餐,相比数组,链表也存在如下劣势:

  • 数据访问效率低,需要移动指针找到想要的数据,不像数组可以直接通过索引获取数据。
  • 实现更复杂,且需要多余的内存用于存放指针

因此,到底是使用链表还是数组,应该根据应用场景与需求选择

代码实现

本文利用C++面向对象的特性与模板实现了一个链表类,并实现了插入、删除、查找、拷贝构造、拷贝赋值等基本操作。出于篇幅考虑,完整代码放在了我的github,点击这里查看,本文仅讲解插入、删除、查找部分的代码,并阐述链表对象的析构思路,毕竟C++申请的内存还是需要手动回收的。

结点类实现

// 链表节点类
template<typename T>
class node
{
public:node() : next(nullptr){}node(T val) : data(val), next(nullptr) {}
private:T data;node* next;friend class list<T>;
};

链表的结点类node如上,非常简单,就是每个结点一个存放数据的成员data和一个指针next,同时在node类中将链表类list声明为友元,便于访问node的成员。

链表类声明

链表类list的声明如下,主要实现了以下操作。list类包含两个成员属性head_ptr和length,前者是链表的头指针,后者储存链表的长度。

template<typename T>
class list
{
public:list(); // 构造函数list(const list<T>& l); // 拷贝构造list<T>& operator= (const list<T>& l); // 拷贝赋值 void insert_node(int index, T val); // 在index处插入结点void del_node(int index); // 删除index处结点T get_node(int index); // 获取index处结点值int find(int value); // 查找值value,找到返回index,找不到返回-1int get_length(); // 获取链表长度void push_back(T val); // 在链表尾部插入数据~list(); // 析构函数private:node<T>* head_ptr; // 链表头指针int length; // 链表长度
};

插入实现

对于插入操作,本文将其分为了三种情况

  • 超出索引,抛出异常
  • 插在空链表的头
  • 一般情况
// 在index处插入结点
template<typename T>
void list<T>::insert_node(int index, T val)
{if((index > this->length)) // 超过索引,最多可以插到当前结点的下一个结点,否则就是超过索引{throw runtime_error("index out of this list`s range");}else if((this->head_ptr == nullptr) && (index == 0)) // 插在空链表的头{this->head_ptr = new node<T>;this->head_ptr->next = nullptr;this->head_ptr->data = val;this->length++;} else // 一般情况{node<T>* p1 = this->head_ptr;node<T>* p2 = new node<T>;for(int i = 0; i < index - 1; i++){p1 = p1->next;}p2->data = val;p2->next = p1->next;p1->next = p2;this->length++;}
}

删除实现

删除操作需要注意的是,每个结点都是通过new在堆区申请的内存,因此删除节点需要手动释放其内存。

// 删除index处结点
template<typename T>
void list<T>::del_node(int index)
{node<T>* p1 = this->head_ptr;node<T>* p2 = nullptr;for(int i = 0; i < index - 1; i++){p1 = p1->next;}p2 = p1->next->next;delete p1->next;p1->next = p2;this->length--;
}

索引实现

// 获取index处结点值
template<typename T>
T list<T>::get_node(int index)
{if(index > this->length - 1) // 超过索引{throw runtime_error("index out of this list`s range");}node<T>* p1 = this->head_ptr;for(int i = 0; i < index; i++){p1 = p1->next;}return p1->data;
}

查找实现

// 查找值value,找到返回index,找不到返回-1
template<typename T>
int list<T>::find(int value)
{node<T>* p1 = this->head_ptr;for(int i = 0; i < this->length; i++){if(p1->data == value){return i;}p1 = p1->next;}return -1;
}

析构函数

析构函数需要做的就是释放链表每个节点的内存。

// 析构函数
template<typename T>
list<T>::~list()
{// 清空链表node<T>* p1 = this->head_ptr;node<T>* p2 = p1->next;while(p2 != nullptr){delete p1;p1 = p2;p2 = p2->next;}delete p1;this->length = 0;this->head_ptr = nullptr;}
```c++this->head_ptr;node<T>* p2 = p1->next;while(p2 != nullptr){delete p1;p1 = p2;p2 = p2->next;}delete p1;this->length = 0;this->head_ptr = nullptr;}

数据结构:链表及其C++实现相关推荐

  1. 初学数据结构--链表

    2019独角兽企业重金招聘Python工程师标准>>> 前言 在这一章,我将介绍另外一种非常重要的线性数据结构--链表.在之前介绍的动态数组,栈和队列这三种数据结构,底层其实依托于静 ...

  2. C++数据结构链表的基本操作

    这篇文章主要为大家介绍了C++数据结构链表基本操作的示例过程有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪 首先创建好一个节点 typedef struct node {in ...

  3. c语言仓库管理系统链表,仓库管理系统 C语言 C++ 数据结构 链表 课程设计

    仓库管理系统 C语言 C++ 数据结构 链表 课程设计 #include #include #include #include #define MAX 64 typedef struct node{ ...

  4. 数据结构 - 链表 - 面试中常见的链表算法题

    数据结构 - 链表 - 面试中常见的链表算法题 数据结构是面试中必定考查的知识点,面试者需要掌握几种经典的数据结构:线性表(数组.链表).栈与队列.树(二叉树.二叉查找树.平衡二叉树.红黑树).图. ...

  5. 数据结构链表之符号表,Python3实现——8

    数据结构链表之符号表 符号表的介绍 之前章节介绍的顺序表和链表都是一个节点储存一个元素的表,但在日常生活中我们还有很多一次需要储存成对或多个值的情况,例如: 符号表最主要的目的将一对元素,用一个键和一 ...

  6. 数据结构链表之队列,Python3实现——7

    数据结构链表之队列 队列概述 定义:队列是一种基于先进先出(FIFO)的数据结构,队列只能在一段进行插入和删除操作的结构,第一个进入队列的元素在读取时会第一个被读取 队列可以使用顺序表(Python中 ...

  7. 数据结构链表之栈,Python3简单实现——5

    数据结构链表之栈 栈的概述 定义:栈是一种基于先进后出(FILO)的数据结构,是一种只能在一段进行插入和删除操作的特殊线性表. 引入名词:将数据存入栈的动作称为压栈,将数据取出栈的动作称为弹栈 栈的特 ...

  8. 数据结构链表例程_如何掌握RxJava例程的四个结构

    数据结构链表例程 by Ayusch Jain 通过Ayusch Jain 如何掌握RxJava例程的四个结构 (How to get a grip on the four constructs of ...

  9. 数据结构链表代码_代码简介:链表数据结构如何工作

    数据结构链表代码 Here are three stories we published this week that are worth your time: 这是我们本周发布的三个值得您关注的故事 ...

  10. python创建链表实例_python数据结构链表之单向链表(实例讲解)

    python数据结构链表之单向链表(实例讲解) 单向链表 单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域.这个链接指向链表中的下一个节点,而最后 ...

最新文章

  1. 不要做浮躁的嵌入式系统工程师
  2. xen networking(2)
  3. FLV Extract 1.2.1
  4. Oracle 原理: 公有同义词 和 私有同义词
  5. php中的unbuffered_row,php – 加载数据infile和unbuffered查询错误
  6. 《深入浅出玩转FPGA》笔记
  7. 启动 docker 容器报错 (iptables failed: iptables --wait -t filter -A DOCKER ! -i docker0 -o docker0
  8. Elasticsearch7.15.2 基础概念和基础语法
  9. SSM(Spring+Spring MVC+Mybatis)整合 1:整体概述、目录内容及实验环境介绍
  10. hantzsch酯_有机人名反应——Hantzsch吡啶合成
  11. flash实用工具类+开源包收藏
  12. 浏览器了解(七)Layout
  13. 回调函数Callback
  14. Python猴子补丁
  15. 原理图编译出现Has no driving source 啥意思?
  16. Kubernetes pull requests
  17. ssd测试软件寿命查看,铅锤哥:怎么看SSD还能用多久?固态硬盘寿命的检测方法...
  18. (转)CGJ02、BD09、西安80、北京54、CGCS2000常用坐标系详解
  19. 计算机专业的一些推荐书籍
  20. [转]C语言图形编程(三) -绘图函数②

热门文章

  1. 查看Linux内核配置文件(.config)
  2. rdkitpython | 化合物去盐
  3. Oracle 11gR2 RAC安装
  4. 使用docker搭建elk
  5. 一年只一次,不点悔一年!
  6. 线上采购成主流,非接触采购助推企业突破疫情困局
  7. 哈尔滨工业大学-计算机系统-2022春-大作业
  8. vSAN实践经验-07: vSAN的监控和告警
  9. TCGA系列--TCGA长链非编码RNA的可视化工具TANRIC
  10. java 插入到完整的classbody_java语法错误,将“}”插入到完整 MethodBody 中