【链表】单链表的实现

简介: 【链表】单链表的实现

前言


链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。链表的结构总共有8种,我们这里来进行单链表的增删查改实现


对单链表的理解

f1330f149a8446d4ac98200b33ef6cd9.png


单链表就像是一辆火车,一个节点一个节点相链接,当获得了头结点的地址后,便可通过单链表特有的结构体找到接下来所链接的所有节点。


下面请看源码,源码下面有四个相关问题及相应解答

若要批评建议请不吝评论,不懂可以私聊我


头文件


// slist.h
typedef int SLTDateType;
typedef struct SListNode
{
  SLTDateType data;
  struct SListNode* next;
}SListNode;
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
// 为什么不在pos位置之前插入?
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
// 为什么不删除pos位置?
void SListEraseAfter(SListNode* pos);
// 单链表的销毁
void SListDestroy(SList* plist);


源代码

// slist.c
#include "SList.h"
 //动态申请节点
SListNode* BuySListNode(SLTDateType x)
{
  SListNode* node = (SListNode*)malloc(sizeof(SListNode));  // 开辟内存
  node->data = x;
  node->next = NULL;
  return node;
}
 //打印输出
void SListPrint(SListNode* plist)
{
  SListNode* cur = plist; // 利用cur进行遍历,对*plist不修改
  while (cur) 
  //while (cur != NULL)
  {
    printf("%d->", cur->data);
    cur = cur->next; //遍历
  }
  printf("NULL\n");
}
 //尾插
void SListPushBack(SListNode** pplist, SLTDateType x) // 要实现对指针*pplist进行修改,就要传入*pplist的地址,即双指针
{
  SListNode* newnode = BuySListNode(x); //动态申请节点
  if (*pplist == NULL) //先判断链表是否为空
  {
    *pplist = newnode;
  }
  else
  {
    SListNode* tail = *pplist; //利用tail对链表进行遍历
    while (tail->next != NULL) //当tail->next为空即tail已指向链表尾部
    {
      tail = tail->next;
    }
    tail->next = newnode;
  }
}
 //尾删
void SListPopBack(SListNode** pplist) //同上双指针的作用
{
  SListNode* prev = NULL;   //prev作用是对tail指向地址进行保存,从而进行尾删
  SListNode* tail = *pplist;
  // 1.空或只有一个节点
  // 2.两个及以上的节点
  if (tail == NULL || tail->next == NULL)
  {
    free(tail);
    *pplist = NULL;
  }
  else
  {
    while (tail->next)
    {
      prev = tail;
      tail = tail->next;
    }
    free(tail); //释放尾部节点空间
    tail = NULL;
    prev->next = NULL; //对新尾部节点next置空
  }
}
 // 头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
  assert(pplist); //断言pplist,若是空指针程序直接结束
  // 1.空
  // 2.非空
  SListNode* newnode = BuySListNode(x); //申请动态节点
  if (*pplist == NULL) //两种情况 
  {
    *pplist = newnode;
  }
  else
  {
    newnode->next = *pplist;  //头插实现
    *pplist = newnode;  //更新头指针pplist指向新地址
  }
}
 //头删
void SListPopFront(SListNode** pplist)
{   //三种情况
  // 1.空
  // 2.一个
  // 3.两个及以上
  SListNode* first = *pplist; 
  if (first == NULL) //若为空
  {
    return;
  }
  else if (first->next == NULL) //只有一个节点
  {
    free(first);
    *pplist = NULL;
  }
  else  //两个及以上
  {
    SListNode* next = first->next;
    free(first);
    *pplist = next;
  }
}
 //查找
SListNode* SListFind(SListNode* plist, SLTDateType x) 
{
  SListNode* cur = plist;
  while (cur) //当cur指针为空时,即已遍历完最后一个
  {
    if (cur->data == x)
      return cur;
    cur = cur->next;
  }
  return NULL;
}
 //单链表在pos位置之后插入x
 //若要实现pos位置之前插入x,需要遍历找到pos之前节点地址,才能进行链接
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
  assert(pos);//断言pos是否为空指针
  SListNode* next = pos->next; //对pos之后节点地址进行保存,便于后续链接
  // pos newnode next
  SListNode* newnode = BuySListNode(x); 
  pos->next = newnode; //进行链接
  newnode->next = next;
}
 // 删除pos位置之后节点
 //要实现删除pos节点,同样需要找到上一个节点进行链接,这是单链表的局限性,而双向循环链表则可以很方便的进行删除和链接
void SListEraseAfter(SListNode* pos)
{
  assert(pos); //断言pos指针
  //对pos之后 pos->next->next 进行保存,便于后续删除pos位置后节点和pos节点进行链接
  SListNode* next = pos->next; 
  if (next != NULL)
  {
    SListNode* nextnext = next->next;
    free(next);
    pos->next = nextnext; //链接
  }
}


为什么有的要asser断言指针变量?

答:防止传入空指针而程序内容进行修改,若为空指针则直接程序结束


为什么头删尾删头插尾插函数要传入双指针?

答:要对指针变量而不是指针所指向的结构体或说内存空间进行修改,就要传入双指针,指向指针的指针对指针变量plist进行修改


为什么不在pos位置之前插入?

答:若要实现pos位置之前插入x,需要遍历找到pos之前节点地址,才能进行链接。


为什么不删除pos位置?

要实现删除pos节点,同样需要找到上一个节点进行链接,这是单链表的局限性,而双向循环链表则可以很方便的进行删除和链接。


本节完

相关文章
|
2月前
|
存储
单链表专题(冲冲冲)(下)
单链表专题(冲冲冲)(下)
44 0
|
2月前
|
存储
单链表专题(冲冲冲)(上)
单链表专题(冲冲冲)(上)
43 0
|
6月前
|
存储 算法
单链表的应用
单链表的应用
46 6
|
6月前
|
存储
单链表专题
单链表专题
42 4
|
7月前
|
存储 编译器
单链表与双链表实现
单链表与双链表实现
|
7月前
|
存储 C语言
单链表详解
单链表详解
128 0
|
7月前
|
存储 缓存
详解单链表
详解单链表
75 0
|
存储 编译器
【链表之单链表】
什么是链表 概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。 通俗地讲,就是一个结构体有两个成员,一个是存放数据的成员,一个是指针成员,通常的单链表是第一个节点的指针成员存着下一个节点的地址,下一个节点的指针成员又存下一个节点的地址,这样每个节点之间连接起来,就叫做链表。 本文主要讲的是链表中的一种重要的形式:单链表。
|
存储
单链表
单链表
|
存储 API 索引
链表——单链表
单向链表是链表的一种,它由多个结点组成,每个结点都由一个数据域和一个指针域组成,数据域用来存储数据,指针域用来指向其后继结点。链表的头结点的数据域不存储数据,指针域指向第一个真正存储数据的结点。
96 0
链表——单链表