线性表--顺序表--单向链表(四)

时间:2022-07-28
本文章向大家介绍线性表--顺序表--单向链表(四),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

开头小故事

在讲链表之前,先讲一个小故事: 在很久很久以前,我从集市上买了一条哈士奇,众所周知,哈士奇,俗称撒手没,为了不让他跑掉,我需要一条链子来拴住它,但是我已经回家了,怎么办呢? 我需要自己动手来做一条,一条链子由什么组成?当然是一节节小链子,链子的样子你一定见过,每一节都有一个小口用来连接下一节,于是我做好了一节节的小链子,并一节一节连起来,然后,我们还需要确定哪一端是人拿的,哪一端是给狗套的,于是我又做了一根绳子接在了链子其中的一段。后来,二哈给我准备了一个20万的装修计划,,,,,,好了,小故事讲完了。配张图!!

正题之前,请先记住链子,小链子,绳子这三个词和对应的链表中的名词。

链表对应名词

链子

链表

小链子

节点

绳子

头节点

现在来讲讲单向链表,首先,链表和数组不同,数组中元素的内存地址是连续的,而链表中的元素内存地址是随机的。由于链表中的节点的数据类型是相同的,并且是随机存储,所以我们不能像数组那样只需要知道头节点就可以访问整个链表,我们必须在每个节点中存储下一个节点的地址,就好比狗链子中每一节的口子,用来连接下一节链子,只有这样,我们才能一节一节访问整个链表。 数组是这样使用内存,内存地址是连续的

而链表是这样使用内存的,所有他们必须有指向下一节点的指针

看到到这里,你对链表应该有一个小的认识了吧,那么下面我们就来定义一个链表吧。

1.定义链表

 typedef struct list
 {
  int x;
  struct list * next;
 }list;

想一想,定义了链表就相当于我们有了每节小链子的模具,接下来,我们就该造小链子以及绳子了。

2.头节点初始化

list * InitListHead() //初始化 
{
 list *Phead = (list *)malloc(sizeof(list));//创建头结点
 Phead->x = 0;  //该变量可存放该链表长度
 Phead->next = NULL;
 return Phead;       //创建后返回该头指针
}

头节点的数据可以存放比如链表的长度等等,接下来,我们只需要重复创建节点,这便是链表,这里要注意一下的是,单向链表包括头插入和尾插入,下面将分开讲解。

3.头插法

void HeadList(list * L,int e)//头插法
{
 list * Pbady = (list *)malloc(sizeof(list));//创建一个节点
 Pbady->x = e;
 Pbady->next = NULL;
 if (L->next == NULL)
 {
  L->next=Pbady;
 }
 else
 {
  Pbady->next = L->next;
  L->next = Pbady;
 }
 L->x++;
}
}

避免生硬的代码,我们用图来说明。

不难看出,使用头插法的数据是逆序排列的,后来的反而排在前面,这也是头插和尾插的区别之一。

4.尾插法

void TrailList(list * LA,list ** LB,int e)//尾插法
{
  list *Pbady = (list*)malloc(sizeof(list));
  Pbady->x = e;
  if (LA->next == NULL)
  {
   LA->next = Pbady;
  }
  else
  {
   (*LB)->next = Pbady;
  }
  Pbady->next = NULL;
  *LB = Pbady;
  LA->x++;
}

尾插法相对于头插法多了一个指针,如果代码看着懵,那我们依旧上图来说。

尾插法不像头插法,只需要在开头插入就可以,而尾插入需要遍历整个链表并找到最后,如果链表足够长,每次都遍历一遍,显然不科学,所以我们想到用一个指针来随时记录最后一个节点的位置,并指向最后一个节点,这样,每当我们有新节点的时候,只需要在该指针后面插入即可。并更新该指针,至于为什么使用二级指针可查看该文。

指针作形参的一些问题

接下来来看一下链表的基本运算。

5.按序号查找内容

int NumFindList(list *L,int e)//按序号查找
{
 if (e <= 0)
 {
  return -1;
 }
 int i = 0;
 int j = L->x;
 list * P = L;
 while (P != NULL&&i <= j)
 {
  if (i == e)
  {
   printf("该内容为%dn", P->x);
   break;
  }
  P = P->next;
  i++;
 }
}

6.按值查找

void ValueFindList(list * L,int e)//按值查找
{
 int i = 0;
 int j = L->x;
 list * P = L;
 while (P != NULL&&i <= j)
 {
  if (P->x == e)
  {
   printf("已找到n");
  }
  P = P->next;
  i++;
 }
}

7.求链表长度

int LengthList(list * L)//链表长度
{
 return L->x;//这里我们只需要访问头节点中的数据即可
}

8.插入操作

void InsertList(list * L,int i,int e)//插入操作
{
 if (i > L->x)
 {
  printf("插入位置不合法n");
  return ;
 }
 list * Pbady = (list *)malloc(sizeof(list));
 list *P = L->next;
 int a = 1;
 Pbady->x = e;
 while (P != NULL)
 {
  if (a == i-1)
  {
   Pbady->next = P->next;
   P->next = Pbady;
   L->x++;
   break;
  }
  P = P->next;
  a++;
 }
}

9.删除操作

void DelList(list * L,int i)//删除操作
{
 if (i > L->x)
 {
  printf("插入位置不合法n");
  return;
 }
 list * P = L->next;
 list * P_2 = NULL;
 int a = 1;
 while (P != NULL)
 {
  if (a == i-1)
  {
   P_2 = P->next;
   P->next = P->next->next;
   P_2->next = NULL;
   free(P_2);
   L->x--;
   break;
  }
  P = P->next;
  a++;
 }
}

10.显示链表现有内容

void ShowList(list * L)//显示链表
{
 list * P = L;
 printf("该链表共有%d个元素n", P->x);
 P = L->next;
 while (P!= NULL)
 {
  printf("%dn", P->x);
  P = P->next;
 }
}

11.释放内存

void FreeList(list * L)
{
 list * P;
 if (L == NULL)
 {
  return ;
 }
 while (L)
 {
  P = L->next;
  free(L);
  L = P;
 }
}

好了,单向链表的讲解就到这里。

如果有什么错误,欢迎指出,欢迎讨论。 每文一句:不要总是用眼泪去打动去挽留,有时侯你的眼泪适得其反,微笑,一定要自信的微笑着面对一切!