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

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

前言

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


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

相关文章
|
6天前
|
存储 Java 索引
【数据结构】链表从实现到应用,保姆级攻略
本文详细介绍了链表这一重要数据结构。链表与数组不同,其元素在内存中非连续分布,通过指针连接。Java中链表常用于需动态添加或删除元素的场景。文章首先解释了单向链表的基本概念,包括节点定义及各种操作如插入、删除等的实现方法。随后介绍了双向链表,说明了其拥有前后两个指针的特点,并展示了相关操作的代码实现。最后,对比了ArrayList与LinkedList的不同之处,包括它们底层实现、时间复杂度以及适用场景等方面。
28 10
【数据结构】链表从实现到应用,保姆级攻略
|
28天前
|
存储 C语言
【数据结构】c语言链表的创建插入、删除、查询、元素翻倍
【数据结构】c语言链表的创建插入、删除、查询、元素翻倍
【数据结构】c语言链表的创建插入、删除、查询、元素翻倍
|
2月前
【数据结构OJ题】环形链表
力扣题目——环形链表
30 3
【数据结构OJ题】环形链表
|
1月前
【数据结构】双向带头(哨兵位)循环链表 —详细讲解(赋源码)
【数据结构】双向带头(哨兵位)循环链表 —详细讲解(赋源码)
29 4
|
2月前
【数据结构OJ题】复制带随机指针的链表
力扣题目——复制带随机指针的链表
42 1
【数据结构OJ题】复制带随机指针的链表
|
2月前
【数据结构OJ题】环形链表II
力扣题目——环形链表II
19 1
【数据结构OJ题】环形链表II
|
2月前
【数据结构OJ题】相交链表
力扣题目——相交链表
23 1
【数据结构OJ题】相交链表
|
22天前
|
存储 Java 程序员
"揭秘HashMap底层实现:从数组到链表,再到红黑树,掌握高效数据结构的秘密武器!"
【8月更文挑战第21天】HashMap是Java中重要的数据结构,采用数组+链表/红黑树实现,确保高效查询与更新。构造方法初始化数组,默认容量16,负载因子0.75触发扩容。`put`操作通过计算`hashCode`定位元素,利用链表或红黑树处理冲突。`get`和`remove`操作类似地定位并返回或移除元素。JDK 1.8优化了链表转红黑树机制,提升性能。理解这些原理能帮助我们更高效地应用HashMap。
29 0
|
24天前
|
存储 算法
【初阶数据结构篇】顺序表和链表算法题
此题可以先找到中间节点,然后把后半部分逆置,最近前后两部分一一比对,如果节点的值全部相同,则即为回文。
|
24天前
|
存储 测试技术
【初阶数据结构篇】双向链表的实现(赋源码)
因为头结点的存在,plist指针始终指向头结点,不会改变。