单链表的实现

简介: 单链表的实现

单链表的定义


       由于顺序表的插入删除操作需要移动大量的元素,影响了运行效率,因此引入了线性表的链式存储——单链表。单链表通过一组任意的存储单元来存储线性表中的数据元素,因此它不要求在逻辑上相邻的两个元素在物理位置上也相邻。


单链表的实现

初始化

我们要先定义一个单链表的结构体,单链表需要一个链表的指针,链表中数据的个数,链表的空间大小,

typedef int SQDatatype;
typedef struct SeqList
{
  SQDatatype* ps;
  int size;
  int capacity;
}SL;


初始化链表时链表指针指向空,链表内元素个数为0,链表空间为0;


//初始化
void SLInit(SL* s1)
{
  assert(s1);
  s1->ps = NULL;
  s1->size = 0;//链表内数据个数
  s1->capacity = 0;//链表空间
}


检查扩容


       每次插入数据时我们都要检查一下链表的空间是否足够,因此可以专门下一个检查扩容的函数;

void SLCheckCapacity(SL* s1)
{
  assert(s1);
  //链表内个数和链表空间相等时需要扩容
  if (s1->size == s1->capacity)
  {
    int newcapacity = s1->capacity == 0 ? 4 : s1->capacity * 2;
    SQDatatype* tem = (SQDatatype*)realloc(s1->ps, newcapacity * sizeof(SQDatatype));
    if (tem == NULL)
    {
      perror("realloc fail");
      return;
    }
    s1->ps = tem;
    s1->capacity = newcapacity;
  }
}


       注意扩容时要用realloc而不是malloc,malloc是重新开辟空间,会覆盖之前的数据,(因为我之前写的时候,就写错成了malloc,检查了老半天);


头插


//头插
void SLPushFront(SL* s1, SQDatatype x)
{
  assert(s1);
  SLCheckCapacity(s1);
  for (int i = s1->size; i > 0; i--)
  {
    s1->ps[i] = s1->ps[i - 1];
  }
  s1->ps[0] = x;
  s1->size++;
}

  头插时把原来的元素一个个往后移,然后头插入新的元素,记得s1->size++ ,注意细节;

尾插


//尾插
void SLPushBack(SL* s1, SQDatatype x)
{
  assert(s1);
  SLCheckCapacity(s1);
  s1->ps[s1->size] = x;
  s1->size++;
}


头删

       删除数据时链表不能为空;

//头删
void SLPopFront(SL* s1)
{
  assert(s1);
  assert(s1->size != 0);
  for (int i = 0; i < s1->size - 1; i++)
  {
    s1->ps[i] = s1->ps[i + 1];
  }
  s1->size--;
}

尾删

       删除数据时链表不能为空;

//尾删
void SLPopBack(SL* s1)
{
  assert(s1);
  assert(s1->size != 0);
  s1->size--;
}

指定位置插入

       注意判断pos的值,如果pos不在0到size中,会直接报错,所以要加个断言

//指定位置插入
void SLInsert(SL* s1, int pos, SQDatatype x)
{
  assert(s1);
  assert(pos >= 0 && pos <= s1->size);
  SLCheckCapacity(s1);
  for (int i = s1->size; i > pos; i--)
  {
    s1->ps[i] = s1->ps[i - 1];
  }
  s1->ps[pos] = x;
  s1->size++;
 
}


指定位置删除

       注意判断pos的值,如果pos不在0到size中,会直接报错,所以要加个断言

//指定位置删除
void SLErase(SL* s1, int pos)
{
  assert(s1);
  assert(pos >= 0 && pos <= s1->size);
  for (int i = pos; i < s1->size - 1; i++)
  {
    s1->ps[i] = s1->ps[i + 1];
  }
  s1->size--;
}

总结(废话)


       单链表是动态的顺序表,对于初学者可以锻炼自己的代码能力,当你熟悉之后就会发现很简单,难点就是初学时不清楚细节,还是要多多练习;

       一起加油!

完整代码

#pragma once
 
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
 
typedef int SQDatatype;
typedef struct SeqList
{
  SQDatatype* ps;
  int size;
  int capacity;
}SL;
//初始化
void SLInit(SL* s1);
//检查扩容
void SLCheckCapacity(SL* s1);
//头插
void SLPushFront(SL* s1, SQDatatype x);
//尾插
void SLPushBack(SL* s1, SQDatatype x);
//头删
void SLPopFront(SL* s1);
//尾删
void SLPopBack(SL* s1);
//指定位置插入
void SLInsert(SL* s1, int pos, SQDatatype x);
//指定位置删除
void SLErase(SL* s1, int pos);
//查找数据
void SLFind(SL* s1, SQDatatype x);
//修改数据
void SLModity(SL* s1, int pos, SQDatatype x);
//打印链表
void SLPrintf(SL* s1);
//销毁链表
void SLDestroy(SL* s1);


#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
 
void SLInit(SL* s1)
{
  assert(s1);
  s1->ps = NULL;
  s1->size = 0;//链表内数据个数
  s1->capacity = 0;//链表空间
}
 
void SLCheckCapacity(SL* s1)
{
  assert(s1);
  //链表内个数和链表空间相等时需要扩容
  if (s1->size == s1->capacity)
  {
    int newcapacity = s1->capacity == 0 ? 4 : s1->capacity * 2;
    SQDatatype* tem = (SQDatatype*)realloc(s1->ps, newcapacity * sizeof(SQDatatype));
    if (tem == NULL)
    {
      perror("realloc fail");
      return;
    }
    s1->ps = tem;
    s1->capacity = newcapacity;
  }
}
//头插
void SLPushFront(SL* s1, SQDatatype x)
{
  assert(s1);
  SLCheckCapacity(s1);
  for (int i = s1->size; i > 0; i--)
  {
    s1->ps[i] = s1->ps[i - 1];
  }
  s1->ps[0] = x;
  s1->size++;
}
//尾插
void SLPushBack(SL* s1, SQDatatype x)
{
  assert(s1);
  SLCheckCapacity(s1);
  s1->ps[s1->size] = x;
  s1->size++;
}
 
//头删
void SLPopFront(SL* s1)
{
  assert(s1);
  assert(s1->size != 0);
  for (int i = 0; i < s1->size - 1; i++)
  {
    s1->ps[i] = s1->ps[i + 1];
  }
  s1->size--;
}
//尾删
void SLPopBack(SL* s1)
{
  assert(s1);
  assert(s1->size != 0);
  s1->size--;
}
//指定位置插入
void SLInsert(SL* s1, int pos, SQDatatype x)
{
  assert(s1);
  assert(pos >= 0 && pos <= s1->size);
  SLCheckCapacity(s1);
  for (int i = s1->size; i > pos; i--)
  {
    s1->ps[i] = s1->ps[i - 1];
  }
  s1->ps[pos] = x;
  s1->size++;
 
}
//指定位置删除
void SLErase(SL* s1, int pos)
{
  assert(s1);
  assert(pos >= 0 && pos <= s1->size);
  for (int i = pos; i < s1->size - 1; i++)
  {
    s1->ps[i] = s1->ps[i + 1];
  }
  s1->size--;
}
//查找数据
void SLFind(SL* s1, SQDatatype x)
{
  assert(s1);
  int flag = 0;
  for (int i = 0; i < s1->size; i++)
  {
    if (s1->ps[i] == x)
    {
      flag = 1;
      printf("找到了,在下表为%d的位置\n", i);
 
    }
  }
  if(flag==0)
  printf("该数据不存在\n");
}
 
//修改数据
void SLModity(SL* s1, int pos, SQDatatype x)
{
  assert(s1);
  assert(pos >= 0 && pos <= s1->size);
  s1->ps[pos] = x;
}
//打印链表
void SLPrintf(SL* s1)
{
  assert(s1);
  for (int i = 0; i < s1->size; i++)
  {
    printf("%d ", s1->ps[i]);
  }
  printf("\n");
}
//销毁链表
void SLDestroy(SL* s1)
{
  assert(s1);
  free(s1->ps);
  SLInit(s1);
}
 
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
int main()
{
  SL s1;
  SLInit(&s1);
  SLPushFront(&s1, 1);
  SLPushFront(&s1, 2);
  SLPushFront(&s1, 3);
  SLPushFront(&s1, 4);
  SLPushFront(&s1, 5);
  SLPushBack(&s1, 100);
  SLPushBack(&s1, 200);
  SLPushBack(&s1, 300);
  SLPushBack(&s1, 400);
  SLPushBack(&s1, 500);
  /*SLPopBack(&s1);
  SLPopBack(&s1);
  SLPopBack(&s1);
  SLPopBack(&s1);*/
  /*SLErase(&s1, 2);*/
  SLModity(&s1, 3, 5000);
  SLFind(&s1, 5000);
  SLPrintf(&s1);
  SLDestroy(&s1);
  return 0;
}


相关文章
|
10月前
|
网络协议 Unix Linux
精选2款C#/.NET开源且功能强大的网络通信框架
精选2款C#/.NET开源且功能强大的网络通信框架
364 0
|
SQL JSON 关系型数据库
"SQL老司机大揭秘:如何在数据库中玩转数组、映射与JSON,解锁数据处理的无限可能,一场数据与技术的激情碰撞!"
【8月更文挑战第21天】SQL作为数据库语言,其能力不断进化,尤其是在处理复杂数据类型如数组、映射及JSON方面。例如,PostgreSQL自8.2版起支持数组类型,并提供`unnest()`和`array_agg()`等函数用于数组的操作。对于映射类型,虽然SQL标准未直接支持,但通过JSON数据类型间接实现了键值对的存储与查询。如在PostgreSQL中创建含JSONB类型的表,并使用`-&gt;&gt;`提取特定字段或`@&gt;`进行复杂条件筛选。掌握这些技巧对于高效管理现代数据至关重要,并预示着SQL在未来数据处理领域将持续扮演核心角色。
233 0
|
机器学习/深度学习 算法 计算机视觉
蔚来感知算法岗面经
蔚来感知算法岗面经
295 0
|
算法 C++
点集合的三角剖分
点集合的三角剖分
88 0
36Echarts - 柱状图(极坐标系下的堆叠柱状图)
36Echarts - 柱状图(极坐标系下的堆叠柱状图)
132 0
【每日一题Day115】LC2335装满杯子需要的最短总时长 | 贪心
【每日一题Day115】LC2335装满杯子需要的最短总时长 | 贪心
66 0
|
JavaScript 前端开发
Vue3 word如何转成pdf代码实现
Vue3 word如何转成pdf代码实现
1472 0
132Echarts - 路径图(A Hiking Trail in Hangzhou - Baidu Map)
132Echarts - 路径图(A Hiking Trail in Hangzhou - Baidu Map)
104 0
|
算法 索引
【算法挨揍日记】day08——30. 串联所有单词的子串、76. 最小覆盖子串
给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如,如果 words = [&quot;ab&quot;,&quot;cd&quot;,&quot;ef&quot;], 那么 &quot;abcdef&quot;, &quot;abefcd&quot;,&quot;cdabef&quot;, &quot;cdefab&quot;,&quot;efabcd&quot;, 和 &quot;efcdab&quot; 都是串联子串。 &quot;acdbef&quot; 不是串联子串,因为他不是任何 words 排列的连接。
444 0
|
数据库 Docker 容器
Docker Swarm 节点标签
Docker Swarm 节点标签
418 0