【数据结构和算法】认识线性表中的链表,并实现单向链表(上)

简介: 【数据结构和算法】认识线性表中的链表,并实现单向链表(上)

前言

我们知道了数据结构中线性表的概念,我们应该会感觉比较好理解,因为顺序表的建立主要涉及到结构体和动态内存管理函数,是类似于数组的一种形式。

我们要思考这样一个问题

1.增容需要申请新空间,拷贝数据,释放旧空间,会有不小的消耗。

2.增容一般都是2倍扩容,有时候也会浪费一定的空间

于是,为了解决上面这样的问题,我们引入了线性表中的链表,这一概念。


一、链表是什么?

1.链表的概念和结构

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

图示如下:

由上图可知,链表的特征为:

1.链式结构在逻辑上是连续的,但是在物理上是不一定连续的。        (每一个结点的地址是不一定的)

2.现实中的结点一般都是从堆中申请出来的。

3.从堆上申请的空间,是按照一定策略来分配,根据编辑器的不同而不同,再次申请的空间可能连续,也可能不连续。

2.链表的分类

实际上链表的结构有很多中,以下组合起来有8种主要的链表结构的情况

1.单向或者双向

2.带头或者不带头

头节点使用的话,就不需要对其数据域赋值,只起到一个成为建立链表的基点的作用,不使用的话,第一个结点存储数值就可以,创建一个新节点给这个第一个结点phead即可,这样头节点链表,就变成了非头结点的链表    

主要就是看第一个结点是否用到了其数值域

用第一结点数据域        非头节点

没用                             头节点

3.循环或者非循环

以上这些类型情况,我们常用的有两种,无头单向非循环链表,带头双向循环链表

如图所示

1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。

二、链表的实现

1.无头单向非循环链表

结构体为:

typedef int SLDataType;
//单向链表的实现、
typedef struct ListNode {
  SLDataType data;//数据域
  struct ListNode* next;//指针域
}List;

要实现的函数为:

//打印单链表
void ListPrint(List* ps);
//单链表的尾插
void ListPushBack(List** ps, SLDataType data);
//单链表的头插
void ListPushFront(List** ps, SLDataType data);
//单链表的尾删
void ListPopBack(List** ps);
//单链表的头删
void ListPopFront(List** ps);
//单链表的查找
List* ListFind(List* ps);
//在pos位置上插入数据
void ListInsertBefore(List** ps, SLDataType x, List* pos);
//在pos位置之后插入数据
void ListInsertAfter(List** ps, SLDataType x, List* pos);
//在pos位子删除数据
void ListErase(List** ps, List* pos);
//在pos位置之后一位删除数据
void ListEraseAfter(List* pos);
//单链表的摧毁
void ListDestory(List** ps);

2.函数功能的实现

1.初始化和打印链表

//初始化链表
void InitList(List* ps) {
  ps->data = 0;
  ps->next = NULL;
}
//打印单链表
void ListPrint(List* ps) {
  List* cur = ps;
  while ((cur) != NULL) {
    printf("%d -> ", cur->data);
    cur = cur->next;
  }
  printf("NULL\n");
}

2.头插和尾插

尾部插入图示如下:

代码如下:

//创建一个新节点
List* CreateNode(SLDataType x) {
  List* newNode = (List*)malloc(sizeof(List));
  if (newNode == NULL) {
    perror("malloc fail\n");
    exit(-1);
  }
  else {
    newNode->data = x;
    newNode->next = NULL;
  }
  return newNode;
}
//单链表的尾插
void ListPushBack(List** ps, SLDataType data) {
  //创建新的节点
  assert(ps);//断言
  List* newNode = CreateNode(data);
  if (*ps == NULL) {
    //说明是空链表
    *ps = newNode;
  }
  else {
    List* tail = *ps;
    while (tail->next != NULL) {
      tail = tail->next;
    }
    tail->next = newNode;
  }
}

头部插入如图所示:

代码如下:

//单链表的头插
void ListPushFront(List** ps, SLDataType data) {
  //先断言是否为空
  assert(ps);
  //将新地址指向头结点下一个next结点的地址,然后在用头结点指向新节点
  List* newNode = CreateNode(data);
  newNode->next = (*ps);  //new指向ps当前的位置,然后new是第一个位置了,将new赋值给ps,这样new就作为头部连接链表了
  (*ps) = newNode;//原本ps位置的数值不变,这样的话就成 new->next=ps,new数值在前,ps的数值在后
}

3.头删和尾删

尾部删除如图所示:

代码演示:

//单链表的尾删
void ListPopBack(List** ps) {
  assert(ps);//断言
  //三种情况
  //1.空链表
  //2.一个节点
  //3.多个节点
  if (*ps == NULL) {
    return;
  }
  //只有一个节点的情况为
  else if ((*ps)->next == NULL) {
    free(*ps); //如果只有一个头节点的话
    *ps = NULL;
  }
  else {
    //多个节点的情况下、
    List* tail = *ps;
    while (tail->next->next!= NULL) {
      tail = tail->next;
    }
    free(tail->next);
    tail->next= NULL;
  }
}

头部删除如图所示:

代码如下:

//单链表的头删
void ListPopFront(List** ps) {
  assert(ps);
  //1.空
  //2.非空
  if (*ps == NULL) {
    //为空
    return;
  }
  else {
    List* tail = (*ps)->next;//创建临时变量tail,将头节点之后的地址给tail
    free(*ps);//滞空头节点
    *ps = NULL;//可有可不有,接下来也要用
    *ps = tail;//将tail也就是ps的下一个List节点给ps
  }
}

4.单链表的查找

代码如下:

//单链表的查找
List* ListFind(List* ps,SLDataType data) {
  //进行查找就是进行判断是否为空链表,为空直接返回
  if (ps == NULL) {
    printf("链表为空、无法查找\n");
    return;
  }
  List* tail = ps;
  while (tail != NULL) {//从头节点开始,进行循环,
    if (tail->data == data) {
      return tail;
    }
    tail = tail->next;
  }
  return tail;//最后还找不到data,tail就为NULL了
}

5.在pos结点位置之前或之后插入数据

在pos结点位置之前插入数据,如图所示:

代码如下:

//在pos位置上插入数据
void ListInsertBefore(List** ps, SLDataType x, List* pos) {
  //先判断是否为空
  assert(ps);
  assert(pos);
  //空链表排除
  //1.pos是第一个节点
  //2.pos不是第一个节点
  if (*ps == pos) {
    //是第一个节点,那就直接头插
    ListPushFront(ps, x);
  }
  else {
    List* prev = *ps;
    while (prev->next != pos) {
      prev = prev->next;
    }
    List* newnode = CreateNode(x);
    prev->next = newnode;
    newnode->next = pos;
  }
}

在pos结点位置之后插入结点,如图所示:

代码如下:

 

//在pos位置之后插入数据
void ListInsertAfter(List** ps, SLDataType x, List* pos) {
  assert(ps);
  //assert(pos);//断言
  List* newnode = CreateNode(x);
  newnode->next = pos->next;
  pos->next = newnode;
}


相关文章
|
4月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
126 29
|
4月前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
178 25
|
5月前
|
存储 算法 测试技术
【C++数据结构——线性表】求集合的并、交和差运算(头歌实践教学平台习题)【合集】
本任务要求编写程序求两个集合的并集、交集和差集。主要内容包括: 1. **单链表表示集合**:使用单链表存储集合元素,确保元素唯一且无序。 2. **求并集**:遍历两个集合,将所有不同元素加入新链表。 3. **求交集**:遍历集合A,检查元素是否在集合B中存在,若存在则加入结果链表。 4. **求差集**:遍历集合A,检查元素是否不在集合B中,若满足条件则加入结果链表。 通过C++代码实现上述操作,并提供测试用例验证结果。测试输入为两个集合的元素,输出为有序集合A、B,以及它们的并集、交集和差集。 示例测试输入: ``` a c e f a b d e h i ``` 预期输出:
157 7
|
5月前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
264 5
|
5月前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】顺序表的基本运算(头歌实践教学平台习题)【合集】
本文档介绍了线性表的基本运算任务,涵盖顺序表和链表的初始化、销毁、判定是否为空、求长度、输出、查找元素、插入和删除元素等内容。通过C++代码示例详细展示了每一步骤的具体实现方法,并提供了测试说明和通关代码。 主要内容包括: - **任务描述**:实现顺序表的基本运算。 - **相关知识**:介绍线性表的基本概念及操作,如初始化、销毁、判定是否为空表等。 - **具体操作**:详述顺序表和链表的初始化、求长度、输出、查找、插入和删除元素的方法,并附有代码示例。 - **测试说明**:提供测试输入和预期输出,确保代码正确性。 - **通关代码**:给出完整的C++代码实现,帮助完成任务。 文档
152 5
|
6月前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
11天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于PSO粒子群优化TCN-LSTM时间卷积神经网络时间序列预测算法matlab仿真
本内容展示了一种基于粒子群优化(PSO)与时间卷积神经网络(TCN)的时间序列预测方法。通过 MATLAB2022a 实现,完整程序运行无水印,核心代码附详细中文注释及操作视频。算法利用 PSO 优化 TCN 的超参数(如卷积核大小、层数等),提升非线性时间序列预测性能。TCN 结构包含因果卷积层与残差连接,结合 LSTM 构建混合模型,经多次迭代选择最优超参数,最终实现更准确可靠的预测效果,适用于金融、气象等领域。
|
7天前
|
算法 数据安全/隐私保护
基于Logistic-Map混沌序列的数字信息加解密算法matlab仿真,支持对文字,灰度图,彩色图,语音进行加解密
本项目实现了一种基于Logistic Map混沌序列的数字信息加解密算法,使用MATLAB2022A开发并包含GUI操作界面。支持对文字、灰度图像、彩色图像和语音信号进行加密与解密处理。核心程序通过调整Logistic Map的参数生成伪随机密钥序列,确保加密的安全性。混沌系统的不可预测性和对初值的敏感依赖性是该算法的核心优势。示例展示了彩色图像、灰度图像、语音信号及文字信息的加解密效果,运行结果清晰准确,且完整程序输出无水印。
基于Logistic-Map混沌序列的数字信息加解密算法matlab仿真,支持对文字,灰度图,彩色图,语音进行加解密
|
7天前
|
算法
基于PSO粒子群优化的多无人机路径规划matlab仿真,对比WOA优化算法
本程序基于粒子群优化(PSO)算法实现多无人机路径规划,并与鲸鱼优化算法(WOA)进行对比。使用MATLAB2022A运行,通过四个无人机的仿真,评估两种算法在能耗、复杂度、路径规划效果及收敛曲线等指标上的表现。算法原理源于1995年提出的群体智能优化,模拟鸟群觅食行为,在搜索空间中寻找最优解。环境建模采用栅格或几何法,考虑避障、速度限制等因素,将约束条件融入适应度函数。程序包含初始化粒子群、更新速度与位置、计算适应度值、迭代优化等步骤,最终输出最优路径。
|
17天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于PSO粒子群优化TCN时间卷积神经网络时间序列预测算法matlab仿真
本内容介绍了一种基于PSO(粒子群优化)改进TCN(时间卷积神经网络)的时间序列预测方法。使用Matlab2022a运行,完整程序无水印,附带核心代码中文注释及操作视频。TCN通过因果卷积层与残差连接处理序列数据,PSO优化其卷积核权重等参数以降低预测误差。算法中,粒子根据个体与全局最优位置更新速度和位置,逐步逼近最佳参数组合,提升预测性能。

热门文章

最新文章