【数据结构】——手撕单链表(流程图+代码版)

目录

一、链表介绍

二、单链表的实现

2.1 应用环境

2.2 单链表的接口实现——增删查改

(1)打印

(2)创造新的节点

(3)头插

(4)头删

(5)尾插

(6)尾删

(7)销毁

(8)查找

(9)在pos之前插入

(10)在pos后面插入

(11)删除pos位置

(12)删除pos后面位置


一、链表介绍

    链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表 中的指针链接次序实现的 。(下图为链式结构的手画简图)

 

【数据结构】——手撕单链表(流程图+代码版)_第1张图片

 但是有三个注意点

1.链式结构在逻辑上是连续的,但在物理上不一定连续

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

3.从对上申请的空间,是按照一定的策略来分配的,两次申请的空间,可能连续,也可能不连续


二、单链表的实现

2.1 应用环境

       无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结 构的子结构,如哈希桶、图的邻接表等等。

2.2 单链表的接口实现——增删查改

typedef int SLTDataType;

typedef struct SListNode
{
       SLTDataType data;
       struct SListNode* next;//这里不能漏写struct,因为此处还没有缩写完成
}SLTNode;

(1)打印

打印的过程其实就是遍历的过程,一个一个访问。

void SListPrint(SLTNode* phead)
{
       //phead是空的很正常,所以此处不用断言
       SLTNode* cur = phead;//将phead赋给cur,是怕后期在遍历过程中找不到头
       while (cur != NULL)
       {
              printf("%d->", cur->data);
              cur = cur->next;
       }
       printf("NULL");
}

(2)创造新的节点

Q:为什么要单独写一段代码创造节点?

A:   在局部定义的话,出了作用域就会自动销毁

SLTNode* BuySLTNode(SLTDataType x)
{
       SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
       if (newnode == NULL)
       {
              perror("malloc fail");
              return -1;
       }
       newnode->data = x;
       newnode->next = NULL;
       
       return newnode;
}

(3)头插

【数据结构】——手撕单链表(流程图+代码版)_第2张图片

 

void SListPushFront(SLTNode** pphead, SLTDataType x)
{
       assert(pphead);
       SLTNode* newnode = BuySLTNode(x);
       newnode->next = *pphead;
       *pphead = newnode;//这样phead就存了newnode的地址,跑前面去了
}

(4)头删

【数据结构】——手撕单链表(流程图+代码版)_第3张图片

 

void SListPopFront(SLTNode** pphead)
{
       assert(pphead);
       //温柔检查
       if (*pphead == NULL)
       {
              return;
       }
       暴力检查
       //assert(*pphead != NULL);//为空可以插入但不能删除
       SLTNode* del = *pphead;
       *pphead = (*pphead)->next;//加括号的原因是两个同优先级
       free(del);//结点是一个一个的,所以每次释放都是一个一个的
       del = NULL;
}

(5)尾插

【数据结构】——手撕单链表(流程图+代码版)_第4张图片

 

void SListPushBack(SLTNode** pphead, SLTDataType x)
{
       assert(pphead);
       SLTNode* newnode = BuySLTNode(x);
       // 情况1.链表本身为空
       // 情况2.链表本身不为空
       if (*pphead == NULL)
       {
              *pphead = newnode;
       }
       else
       {
              // 找尾
              SLTNode* tail = *pphead;
              while (tail->next != NULL)
              {
                      tail = tail->next;
              }
              //先找到最后一位
              tail->next = newnode;
       }
}  

(6)尾删

【数据结构】——手撕单链表(流程图+代码版)_第5张图片

 

void SListPopBack(SLTNode** pphead)
{
       assert(pphead);
       //1.一个节点
       //2.多个节点
       
       //温柔检查
       if (*pphead == NULL)
       {
              return;
       }
       if ((*pphead)->next == NULL)
       {
              free(*pphead);
              *pphead = NULL;
       }
       else
       {
       //方法1
       找尾
       //SLTNode* prev = NULL;
       //SLTNode* tail = pphead;
       //while (tail->next != NULL)
       //{
       //     prev = tail;
       //     tail = tail->next;
       //}
       //prev->next = NULL;
       //free(tail);
       //tail = NULL;
       //方法2
              SLTNode* tail = *pphead;
              while (tail->next->next != NULL)
              {
                      tail = tail->next;
              }
              free(tail->next);
              tail->next = NULL;
       }
       
}

(7)销毁

void SListDestory(SLTNode** pphead)
{
       assert(pphead);
       SLTNode* cur = *pphead;
       while (cur)
       {
              SLTNode* next = cur->next;
              free(cur);
              cur = next;
       }
       *pphead = NULL;
}

(8)查找

SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
       SLTNode* cur = phead;
       while (cur)
       {
              if (cur->data == x)
              {
                      return cur;
              }
              cur = cur->next;
       }
       return NULL;
}

(9)在pos之前插入

【数据结构】——手撕单链表(流程图+代码版)_第6张图片

 

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
       assert(pphead);
       assert(pos);
       if (pos == *pphead)
       {
              SListPushFront(pphead, x);
       }
       else
       {
              SLTNode* prev = *pphead;
              while (prev->next != pos)
              {
                      prev = prev->next;
                      //暴力检查,pos不在链表中,prev为空,还没有找到pos,说明pos传错了
                      assert(prev);
              }
              SLTNode* newnode = BuySLTNode(x);
              prev->next = pos;
              newnode->next = pos;
       }
       
}

(10)在pos后面插入

【数据结构】——手撕单链表(流程图+代码版)_第7张图片

 

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

(11)删除pos位置

【数据结构】——手撕单链表(流程图+代码版)_第8张图片

 

void SListErase(SLTNode** pphead, SLTNode* pos)
{
       assert(pphead);
       assert(pos);
       if (*pphead == pos)
       {
              SListPopFront(pphead);
       }
       else
       {
              SLTNode* prev = *pphead;
              while (prev->next != pos)
              {
                      prev = prev->next;
                      // 检查pos不是链表中节点,参数传错了
                      assert(prev);
              }
              prev->next = pos->next;
              free(pos);
              //pos = NULL;
       }
}

(12)删除pos后面位置

【数据结构】——手撕单链表(流程图+代码版)_第9张图片

 

void SListEraseAfter(SLTNode* pos)
{
       assert(pos);
       if (pos->next == NULL)
       {
              return;
       }
       else
       {
              SLTNode* next = pos->next;
              pos->next = next->next;
              free(next);
       }
}

:如果对您有帮助的话,不要忘记一键三连哦,撒花

你可能感兴趣的:(数据结构,链表)