数据结构——单链表(不带头节点)下

简介: 数据结构——单链表(不带头节点)下

尾删函数

和头删同样,需要传二级指针,以及断言2次


尾删,找到尾节点很简单,但是删除尾节点后还需要把尾节点前一个结点的next指针赋为NULL,但是如何找到这个倒数第二个结点是个问题


这里我们有2种放法:


1.利用tail->next->next找,当tail->next->next==NULL时,就找到了新的尾结点


f987ee9cc6eb4de590a9b352f78bc48d.png





2.定义一个prev指针,让prev一直在tail指针的前面,当tail到达尾时,prev也自然是倒数第二个结点了

起始时:





f581486422af4bb2a4d3d13a29fb70db.png



逐渐向后移动:




c0a6becac500423ea47b2130e82382a2.png



最后:



2f553c66dfcc41a4902df57acefa831a.png



这里我们使用第一种方法,接着我们还会发现一个问题:当只有一个节点时,cur->next已经为空了。cur->next->next就错了

所以还需分类运算当只有一个节点的情况


void SLTPophBack(SLTNode** pphead)
{
  assert(pphead);
  assert(*pphead);
  if ((*pphead)->next == NULL)
  {
  free(*pphead);
  *pphead = NULL;
  }
  else
  {
  SLTNode* tail = *pphead;
  while (tail->next->next != NULL)
  {
    tail = tail->next;
  }
  free(tail->next);
  tail->next = NULL;
  }
}
1


查找函数

遍历链表,如果找到则返回这个节点的地址,否则返回NULL


SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
  SLTNode* pos = phead;
  while (pos != NULL)
  {
  if (pos->data == x)
  {
    return pos;
  }
  else
  {
    pos = pos->next;
  }
  }
  return NULL;
}


pos位置前插入

这个pos是通过SLTFind寻找返回的节点地址,这个地址不会为空,所以可以断言判断一下


如果想在pos位置前插入,就需要直到pos前一个位置,所以这里就需要遍历寻找pos的前一个结点prev,然后将prev,newndoe和pos链接在一起


如果这个pos等于*pphead,就是在头节点前插入,也就是头插


void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
  assert(pphead);
  assert(pos);
  if (pos==*pphead)
  {
  SLTPushFront(pphead, x);
  }
  else
  {
  SLTNode* prev = *pphead;
  while (prev->next != pos)//想要插入pos前,就要知道pos前的节点,就需要遍历,所以单链表不适合在前面插入
  {
    prev = prev->next;
  }
  SLTNode* newnode = SLTBuyNode(x);
  prev->next = newnode;
  newnode->next = pos;
  }
}



因为在pos前插入,所以这个函数是不会做到尾插功能的


下面考虑一个问题:如果只给pos,不给头指针,怎么在pos前插入?


在pos后面插入,再交换pos和pos后面节点中的data值就做到了在pos前面插入


删除pos位置节点

void SLTErase(SLTNode** pphead, SLTNode* pos)
{
  assert(pphead);
  assert(pos);
  assert(*pphead);
  if (pos == *pphead)
  {
  SLTPopFront(pphead);
  }
  else
  {
  SLTNode* prev = *pphead;
  while (prev->next != pos)
  {
    prev = prev->next;
  }
  prev->next = pos->next;
  free(pos);
  pos = NULL;
  }
}



只给pos,不给头指针,也可以删除pos位置节点:交换pos和pos->next的data值,保存pos->next->next的值为nex,然后删除pos->next,最后链接pos和nex

但是这种方法不适用尾删


pos位置后面插入

void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
  assert(pos);
  SLTNode* newnode = SLTBuyNode(x);
  newnode->next = pos->next;
  pos->next = newnode;
}


因为在pos后面插入,所以不需要传头指针,同时还需要assert断言判断pos是否为空

在pos后面插入,所以这个函数实现不了头插


pos位置后面删除

void SLTEraseAfter(SLTNode* pos)
{
  assert(pos);
  assert(pos->next);
  SLTNode* del = pos->next;
  pos->next = pos->next->next;
  free(del);
  del = NULL;
}
1


在pos后面删除,不仅到断言pos还需要断言pos->next


其余逻辑很简单


销毁函数

void SLTDestroy(SLTNode** pphead)
{
  assert(pphead);
  assert(*pphead);
  SLTNode* del = *pphead;
  SLTNode* next = NULL;
  while (del != NULL)
  {
  next = del->next;
  free(del);
  del = next;
  }
  *pphead = NULL;
}


14

因为销毁会改变头指针的指向,所以需要传二级指针


如果链表为空,就不必销毁了,所以需要断言*pphead


销毁链表,是一个一个结点得释放,在释放当前节点前,需要保存下一个节点的地址,然后再销毁当前节点,再删除下一个节点


最后还需要把*pphead也就是头节点赋值为空*pphead = NULL


单链表的问题

从上面的代码中可以看出,对于单链表想要尾插,就需要找尾,想要尾删就需要找到尾和尾的前一个结点

并且在某个位置插入删除,需要找到这个位置的前一个结点

这些操作都需要遍历链表,效率低


这也正是单链表的问题,而这些问题放到带头循环双向链表就是小菜一碟了


目录
相关文章
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
33 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
1月前
|
存储
[数据结构] -- 单链表
[数据结构] -- 单链表
26 1
|
2月前
|
存储 Java
java数据结构,线性表链式存储(单链表)的实现
文章讲解了单链表的基本概念和Java实现,包括头指针、尾节点和节点结构。提供了实现代码,包括数据结构、接口定义和具体实现类。通过测试代码演示了单链表的基本操作,如添加、删除、更新和查找元素,并总结了操作的时间复杂度。
java数据结构,线性表链式存储(单链表)的实现
|
1月前
|
分布式计算 Hadoop Unix
Hadoop-28 ZooKeeper集群 ZNode简介概念和测试 数据结构与监听机制 持久性节点 持久顺序节点 事务ID Watcher机制
Hadoop-28 ZooKeeper集群 ZNode简介概念和测试 数据结构与监听机制 持久性节点 持久顺序节点 事务ID Watcher机制
42 1
|
1月前
|
存储
【数据结构】——单链表实现
【数据结构】——单链表实现
|
1月前
|
存储
数据结构2——单链表
数据结构2——单链表
33 1
|
1月前
|
存储
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)(一)
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)
|
1月前
|
存储
数据结构(单链表)
数据结构(单链表)
17 0
|
1月前
|
存储
数据结构--单链表
数据结构--单链表
|
1月前
|
存储 缓存
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)(二)
【初阶数据结构】深入解析单链表:探索底层逻辑(无头单向非循环链表)