数据结构之链表详解(一)

简介: 即顺序表之后,今天给大家带来另外一个线性结构——链表。

前言

即顺序表之后,今天给大家带来另外一个线性结构——链表。


1.链表的概念及结构

链表是一种 物理存储结构上非连续 、非顺序的存储结构,数据元素的 逻辑顺序 是通过链表

中的 指针链接 次序实现的 。

虽然链表在存储未位置上是不连续的,但在逻辑上其又是连续的,具体结构如下:


2.链表的实现

2.1 准备工作

和顺序表一样,这里我们准备三个文件


Slist.c——对链表相关功能的实现


Slist.h——进行有关函数的声明


Text.c——对有关功能的实现


2.2 链表结构声明

typedef int SLTDateType;
typedef struct SListNode
{
  SLTDateType data;
  struct SListNode* next;
}SListNode;

这里我们用data存储相关内存,用结构体指针next存储下一个节点的位置。


2.3 动态节点的申请

由于链表是通过一个一个节点在逻辑上连续实现的,所以创建节点是我们必须需要的操作,这里我们就将其封装成一个函数,以便每次的使用

SListNode* BuySListNode(SLTDateType x)
{
  SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
  if (newnode == NULL)
  {
  perror("malloc fail");
  return NULL;
  }
  newnode->data = x;
  newnode->next = NULL;
  return newnode;
}

2.4 单链表尾插

void SListPushBack(SListNode** pplist, SLTDateType x)
{
  assert(pplist);
  SListNode*newnode= BuySListNode(x);
  if (*pplist == NULL)
  {
  *pplist = newnode;
  }
  else
  {
  SListNode* tail=*pplist;
  while (tail->next)
  {
    tail = tail->next;
  }
  tail->next = newnode;
  }
}


这里我们采用的是无哨兵位的单链表,所以头指针为空时我们直接将头指针指向新节点,如果头指针后有节点我们就用tail指针找到尾节点,然后让尾节点的next指向新节点实现插入,还有一点是这里我们要传二级指针,由于形参是对实参的复制,所以我们要改变头节点时,只传一级指针时,并不可以改变头节点的指向,比如


image.png


此时头指针s还是指向NULL,我们只是把形参这个指针指向了新节点而已,并没有改变链表结构。


对于具体的尾插操作这里我给大家实际演示一下:

image.png



2.5 打印函数的实现

实现打印函数,以便我们更好的测试我们所实现的函数


void SListPrint(SListNode* plist)
{
  SListNode* cur = plist;
  while (cur)
  {
  printf("%d-> ", cur->data);
  cur = cur->next;
  }
  printf("NULL\n");
}

这里解释一下为什么传的是一级指针,由于我们这里只是打印链表,对链表的头节点不进行改变,所以这里我们直接传一级指针即可。


接下来我们就对我们的后插进行测试:


void text3()

{
  SListNode* s = NULL;
  SListPushBack(&s, 1);
  SListPushBack(&s, 2);
  SListPushBack(&s, 3);
  SListPushBack(&s, 4);
  SListPrint(s);
}
int main()
{
  text3();
  return 0;
}

结果展示:

image.png



2.6 单链表尾删

 


void SListPopBack(SListNode** pplist)
{
  assert(pplist);
  assert(*pplist);
  if ((*pplist)->next==NULL)
  {
    free(*pplist);
    *pplist = NULL;
  }
  else
  {
    SListNode* tail = *pplist;
    while (tail->next->next)
    {
      tail = tail->next;
    }
    free(tail->next);
    tail->next = NULL;
  }
}


这里我给大家解释一下上面的两个断言,首先我们二级指针存储的是单链表头节点的地址,所以该不可能为空,其次由于是进行删除操作,所以单链表不可能为空,所以解引用后,该是不可能为空指针的,所以我们要对该进行断言操作。


其次这里的删除一共有两种情况:


第一是头指针后只有一个节点:


image.png


这里我们只需要把头指针给释放掉就行。


第二种情况就是头指针后不止一个节点,这里我们如果只使用一个指针对其进行删除操作,那么我们只需要找到尾节点的头一个节点,然后通过next释放掉尾节点,将尾节next置空,具体如下

image.png



还有一种我们可以使用两个指针,一个保存尾节点,一个保存尾节点的头一个节点 ,然后释放尾节点,对尾节点头一个节点的next进行置空,具体如下:



image.png


这里代码就不给大家演示,大家可以自己写一写看。


测试如下:


void text3()
{
  SListNode* s = NULL;
  SListPushBack(&s, 1);
  SListPushBack(&s, 2);
  SListPushBack(&s, 3);
  SListPushBack(&s, 4);
  SListPrint(s);
  SListPopBack(&s);
  SListPopBack(&s);
  SListPrint(s);
}
int main()
{
  text3();
  return 0;
}


结果演示:


image.png

相关文章
|
1月前
|
缓存
数据结构之 - 链表数据结构详解: 从基础到实现
数据结构之 - 链表数据结构详解: 从基础到实现
72 6
|
19天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
45 4
|
20天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
20天前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
1月前
|
存储 Java
数据结构第三篇【链表的相关知识点一及在线OJ习题】
数据结构第三篇【链表的相关知识点一及在线OJ习题】
26 7
|
1月前
|
存储 安全 Java
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
23 3
|
1月前
|
算法 Java
数据结构与算法学习五:双链表的增、删、改、查
双链表的增、删、改、查操作及其Java实现,并通过实例演示了双向链表的优势和应用。
16 0
数据结构与算法学习五:双链表的增、删、改、查
|
19天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
39 0
【数据结构】——双向链表详细理解和实现
【数据结构】——双向链表详细理解和实现
|
1月前
|
存储 Java
【数据结构】链表
【数据结构】链表
18 1