秒懂单链表1

简介: 秒懂单链表

首先我们看看我们来看看顺序表有哪些不足:静态顺序表我们就不多说了,我们来看一下动态顺序表。


动态顺序表:①插入数据,空间不够要增容 ②要求数据是依次存储的


缺陷:①如果空间不够,就需要增容。增容会付出一定的性能消耗,其次可能出现一定的性能浪费 ②头部或者中部左右的插入删除效率低------》O(n)


如何去解决它了,那就需要进入我们今天的重头戏——链表


链表的特点:①空间上,按需给空间  ②不要求物理空间连续,头部和中间的插入删除,不需要挪动数据


概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表 中的指针链接次序实现的 。


逻辑结构图:


q1.png


通过以上的逻辑结构图,我们可以看出每个结点都包括两部分:数据和指针,通过指针指向下一个结点。每个结点我们都可以设置为结构体类型。


物理结构图:

q2.png



通过以上的物理结构图,我们可以看除了最后一个结点的指针指向NULL,其余的指针都指向下一个结点的数据域地址。


那我们接下来,做一下对单链表的增删查找等操作。


跟上我的步伐哦!相信各位勇敢者学完定有收获


首先定义一个头文件:


#include<stdio.h>
#include<stdlib.h>
#pragma once
typedef int SLTDataType;
typedef struct SListNode
{
  SLTDataType data;
  struct SListNode* next;
}SLTNode;
//打印
void SListPrint(SLTNode* phead);
//尾插
void SListPushBack(SLTNode** pphead, SLTDataType x);
//头插
void SListPushFront(SLTNode** pphead, SLTDataType x);
//头删
void SListPopFront(SLTNode** pphead);
//尾删
void SListPopBack(SLTNode** pphead);
//查找
SLTNode* SListFind(SLTNode* phead, SLTDataType x);
//在pos的前面插入x
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除pos位置的值
void SListErase(SLTNode** pphead, SLTNode* pos);


这个头文件主要包含了:1.库函数的声明  2.定义了一个SLTNod的结构体类型 3.自定义函数的声明


在定义一个.c的测试文件:


#include"SList.h"
void TestSList1()
{
  SLTNode* plist = NULL;
  SListPushBack(&plist, 1);
  SListPushBack(&plist, 2);
  SListPushBack(&plist, 3);
  SListPushBack(&plist, 4);
  SListPushFront(&plist,5);
  SListPushFront(&plist,6);
  SListPushFront(&plist,7);
  SListPrint(plist);
  SListPopFront(&plist);
  SListPrint(plist);
  SListPopBack(&plist);
  SListPrint(plist);
  //想在3的前面插入一个30
  SLTNode* pos = SListFind(plist, 6);
  if (pos)
  {
  SListInsert(&plist, pos, 10);
  }
  SListPrint(plist);
  pos = SListFind(plist, 10);
  if (pos)
  {
  SListErase(&plist, pos);
  }
  SListPrint(plist);
}
int main()
{
  TestSList1();
  return 0;
}


测试文件:通过主函数的调用TestList1,TestList1中首先定义了一个结构体指针指向SLTNode类型的结构体.


请各位思考一下为什么不定义一个结构体变量,反而定义一个结构体指针了???


如图所示:


q3.png


观图所知,定义一个同类型的结构体指针,可以指向头结点,然后通过头结点的指针域访问第二个结点,依次如下。反而结构体变量却达不到这种效果。


那我们接下来实现头文件中的自定义函数吧!!!


//打印
void SListPrint(SLTNode* phead)
{
  SLTNode* cur = phead;
  while (cur != NULL)
  {
  printf("%d->", cur->data);
  cur = cur->next;
  }
  printf("NULL\n");
}

q4.png


疑惑解答:


疑惑①:大家学到这里是不是有点疑惑,但是千万别划走哦,有些侠士会想连插入都没有学,怎么打印呀!先别急,首先打印操作在单链表中相对其它操作简单一点,所以先让大家有先接受一下打印。


疑惑②:现在大家肯定还有一个疑惑,怎么让phead指针指向头结点地址了。


别急、别急学完插入之后,你就将知道phead指针是怎么指向头结点的地址了


跟上我的步伐,开始学习插入了



//尾插
void SListPushBack(SLTNode** pphead, SLTDataType x)
{
  SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
  newnode->data = x;
  newnode->next = NULL;
  if (*pphead == NULL)
  {
  *pphead = newnode;
  }
  else
  {
  SLTNode* tail = *pphead;
  //找到尾节点的指针
  while (tail->next != NULL)
  {
    tail = tail->next;
  }
  //链接尾节点
  tail->next = newnode;
  }
}


w1.png


学到这里大家,应该知道phead指针是怎样指向头结点的了吧!大家应该也知道如何实现尾插了吧!


大家可以自己先不看下面的讲解,自己先试一试头插,然后写完之后再看看与代码中的头插有哪里不同吧!!!


//头插
void SListPushFront(SLTNode** pphead, SLTDataType x)
{
  SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
  newnode->data = x;
  newnode->next = NULL;
  newnode->next = *pphead;
  *pphead = newnode;
}


w2.png

注:要是链表为空,那我们新增的结点的指针域本来就是空,然后*pphead里面也是空,把*pphead指向新结点,新结点的指针域为空,在赋一个空,不影响


//头删
void SListPopFront(SLTNode** pphead)
{
  SLTNode* next = (*pphead)->next;
  free(*pphead);
  *pphead = next;
}

w3.png

相关文章
|
存储
【单链表】
【单链表】
63 0
|
1月前
|
存储
单链表专题(冲冲冲)(上)
单链表专题(冲冲冲)(上)
35 0
|
1月前
|
存储
单链表专题(冲冲冲)(下)
单链表专题(冲冲冲)(下)
30 0
|
5月前
|
存储 算法
单链表的应用
单链表的应用
41 6
|
5月前
|
存储
单链表专题
单链表专题
39 4
|
6月前
|
存储 编译器
单链表与双链表实现
单链表与双链表实现
|
5月前
|
存储
单链表的实现
单链表的实现
23 0
|
6月前
|
搜索推荐
了解单链表
了解单链表
40 0
|
6月前
|
存储 C语言
单链表详解
单链表详解
110 0
|
6月前
|
存储 缓存
详解单链表
详解单链表
68 0
详解单链表