【数据结构】线性表之——“顺序表”

简介: 前言为什么有顺序表?由于数组定义之后不能扩充,我们需要一种更方便的结构来装载数据,顺序表诞生了。顺序表是线性表的一种,线性表的存储都是形成一条线的,顺序表不仅仅在逻辑上是用一条线链接,在内存中也是连续的一块空间,因此可以进行下标随机访问。顺序表不同于数组,顺序表在空间不够的情况下可以选择进行扩充。顺序表不同于数组,顺序表开辟在堆空间中,不用担心栈空间不够用。相对麻烦的就是,先对顺序表进行操作必须要自己实现增删改查等函数。如果学会了顺序表可以试着自主完成通讯录小程序⭐

前言

  • 为什么有顺序表?由于数组定义之后不能扩充,我们需要一种更方便的结构来装载数据,顺序表诞生了。
  1. 顺序表是线性表的一种,线性表的存储都是形成一条线的,顺序表不仅仅在逻辑上是用一条线链接,在内存中也是连续的一块空间,因此可以进行下标随机访问
  1. 顺序表不同于数组,顺序表在空间不够的情况下可以选择进行扩充
  2. 顺序表不同于数组,顺序表开辟在堆空间中,不用担心栈空间不够用。
  3. 相对麻烦的就是,先对顺序表进行操作必须要自己实现增删改查等函数。
  4. 如果学会了顺序表可以试着自主完成通讯录小程序

PS:需要源码直接通过目录跳转到最后

顺序表主体结构

默认大小与扩容大小还有类型都可以是任意大小或类型

#define SLDataType int
#define DEFAULT_SZ 5      //默认大小
#define DEFAULT_INC 3     //默认扩容大小
typedef struct SeqList
{
  SLDataType* data;
  int sz;
  int capable;
}SeqList;

c55d64265ad54c6099cd4396c2819187.png

用define定义数据可以方便修改

void SLInit(SeqList* SL); 顺序表初始化

void SLprint(SeqList* SL); 打印顺序表

void SLPushFront(SeqList* SL, SLDataType x); 顺序表投插

void SLPushBack(SeqList* SL, SLDataType x); 顺序表尾插

void SLPopFront(SeqList* SL); 顺序表头删

void SLPopBack(SeqList* SL); 顺序表尾删

void SLInsert(SeqList* SL, int pos, SLDataType x); ~指定位置插入

void SLEarse(SeqList* SL, int pos); 指定位置删除

int SLFind(SeqList* SL, SLDataType x); 查找指定数据下标

void SLModify(SeqList* SL, int pos, SLDataType x); 修改指定位置数据

void SLDestroy(SeqList* SL); 清楚数据包

为了代码方便阅读,讲顺序表操作函数全部放在Seqlist.c文件中,将头文件放在SeqList.h,测试文件test.c 🌞

顺序表操作函数实现

实现顺序:

为了方便调试,建议每写完1-2个函数就进行测试,初始化之后,首先实现print函数可以方便我们进行调试,建议按照顺序,插入-》打印-》查找-》删除-》销毁顺序表函数。

顺序表的初始化:

void SLInit(SeqList* SL)
{
  assert(SL);       //assert防止传进来的指针为空,进行断言
  SL->capable = DEFAULT_SZ;
  SL->sz = 0;
  int* ptr = malloc(sizeof(int) * DEFAULT_SZ);  //为顺序表开辟一段空间
  if (ptr == NULL)                //预防开辟失败进行判断
  {
    perror("malloc:");
  }
  SL->data = ptr;
  printf("初始化成功\n");
}

必须先进性初始化,给顺序表开辟一个堆空间,并对其默认长度与默认最大长度进行初始化。

顺序表插入函数:

头插

头插从第一个位置插入,其余函数按顺序向后移动一格,

  • 既然有插入就一定会有空间不够用的问题,这里用到检查空间是否够用的一个函数
void CheckSL(SeqList* SL)
{
  assert(SL); //assert防止传进来的指针为空,进行断言
  if (SL->sz == SL->capable)    //如果当前大小等于当前最大大小那么进行扩容
  {
    int* ptr = NULL;
    ptr = (int*)realloc(SL->data, sizeof(int) * (SL->capable + DEFAULT_INC));
    if (ptr == NULL)
    {
      perror("check:realloc:");
    }
    SL->data = ptr;
    SL->capable += DEFAULT_INC;
    printf("扩容成功\n");
  }
}

头插函数

  • 将所有数据向后移动,注意要从后面开始移动,不然数据会被覆盖掉

50d186b3567943aeba861c4fe3ede86c.gif

void SLPushFront(SeqList* SL, SLDataType x)
{
  assert(SL); //assert防止传进来的指针为空,进行断言
  CheckSL(SL);
  int end = SL->sz - 1;
  while (end >= 0)    //将所有数据向后移动,注意要从后面开始移动,不然数据会被覆盖掉
  {
    SL->data[end + 1] = SL->data[end];
    end--;
  }
  SL->data[0] = x;    //所有数据都向后移动之后进行头插
  SL->sz++;       //当前大小+1
}

尾插

从最后进行插入数据

  • 尾插也会用到检查空间是否够用的函数,这里不重复了
void SLPushBack(SeqList* SL,SLDataType x)
{
  assert(SL); //assert防止传进来的指针为空,进行断言
  CheckSL(SL);      //判断空间是否够用,够用直接将数据放在尾部即可
  SL->data[SL->sz] = x;
  SL->sz++;       //插入之后当前大小+1
}

指定位置插入

  • 只要有插入,就要检查空间是否够用
void SLInsert(SeqList* SL,int pos,SLDataType x)
{
  assert(SL); //assert防止传进来的指针为空,进行断言
  assert(pos >= 0 && pos < SL->sz);
    CheckSL(SL);
  int end = SL->sz - 1;
  while (end >=pos-1)     //讲数据从后向后移动,避免被覆盖
  {
    SL->data[end + 1] = SL->data[end];
    end--;
  }
  SL->data[pos-1] = x;
  SL->sz++;     //插入之后当前大小+1
} 

顺序表打印函数

写完插入立马写打印,方便进行调试

void SLprint(SeqList* SL)
{
  assert(SL); //assert防止传进来的指针为空,进行断言
  for (int i = 0; i < SL->sz; i++)
  {
    printf("%d ", SL->data[i]);
  }
  printf("\n");
}

查找顺序表数据

int SLFind(SeqList* SL, SLDataType x)
{
  assert(SL); //assert防止传进来的指针为空,进行断言
  for (int i = 0; i < SL->sz; i++)
  {
    if (SL->data[i] == x)
    {
      return i;
    }
  }
  return -1;
}

顺序表删除函数

头删

从头部删除数据,删除之后后面的数据依次向前移动一位,要从前往后移动,不然会被覆盖(同头插)

void SLPopFront(SeqList* SL)
{
  assert(SL); //assert防止传进来的指针为空,进行断言
  if (SL->sz <= 0)
  {
    printf("表为空,无需删除");
    return;
  }
  int start = 0;
  while (start < SL->sz - 1)
  {
    SL->data[start] = SL->data[start + 1];
    start++;
  }
  SL->sz--;
}

尾删

void SLPopBack(SeqList* SL)
{
  assert(SL); //assert防止传进来的指针为空,进行断言
  if (SL->sz <= 0)
  {
    printf("表为空,无需删除");
    return;
  }
  SL->sz--;
}

指定位置删除

后面的数据依次向前移动

void SLEarse(SeqList* SL,int pos)
{
  assert(SL); //assert防止传进来的指针为空,进行断言
  assert(pos >= 0 && pos < SL->sz);
  int start = pos-1;
  while (start < SL->sz - 1)
  {
    SL->data[start] = SL->data[start + 1];
    start++;
  }
  SL->sz--;
}

修改顺序表

void SLModify(SeqList* SL, int pos, SLDataType x)
{
  assert(SL); //assert防止传进来的指针为空,进行断言
  assert(pos >= 0 && pos < SL->sz);
  SL->data[pos - 1] = x;
}

销毁顺序表

void SLDestroy(SeqList* SL)
{
  assert(SL); //assert防止传进来的指针为空,进行断言
  free(SL->data);//释放堆空间数据
  SL->data = NULL;//将指针置空预防野指针
  SL->sz = 0;
  SL->capable = 0;
}

文件分类

🌞🌞为了使代码更有阅读性,我们不建议把所有函数写在一个文件里,所以这里分成三个文件,模块化管理

test.c

测试文件

#include "SeqList.h"
void test(SeqList* SL)
{
  SLPushBack(SL, 10);
  SLPushBack(SL, 11);
  SLPushBack(SL, 11);
  SLPushBack(SL, 11);
  SLPushBack(SL, 11);
  SLPushBack(SL, 11);
  SLPushBack(SL, 11);
  SLPushBack(SL, 11);
  SLPushBack(SL, 19);
  SLprint(SL);
  SLPushFront(SL, 12);
  SLPushFront(SL, 13);
  SLPushFront(SL, 14);
  SLprint(SL);
  SLPopBack(SL);
  SLprint(SL);
  SLPopFront(SL);
  SLprint(SL);
  SLInsert(SL, 5, 99);
  SLprint(SL);
  SLModify(SL, 5, 22);
  SLprint(SL);
  //SLEarse(SL, 5);
  //SLprint(SL);
  SLFind(SL,5);
  SLDestroy(SL);
}
int main()
{
  SeqList SL;
  SLInit(&SL);
  test(&SL);
}

SeqList.c

i将所有函数封装在此文件下

#include "SeqList.h"
void SLInit(SeqList* SL)
{
  assert(SL);
  SL->capable = DEFAULT_SZ;
  SL->sz = 0;
  int* ptr = malloc(sizeof(int) * DEFAULT_SZ);
  if (ptr == NULL)
  {
    perror("malloc:");
  }
  SL->data = ptr;
  printf("初始化成功\n");
}
void SLprint(SeqList* SL)
{
  assert(SL);
  for (int i = 0; i < SL->sz; i++)
  {
    printf("%d ", SL->data[i]);
  }
  printf("\n");
}
void CheckSL(SeqList* SL)
{
  assert(SL);
  if (SL->sz == SL->capable)
  {
    int* ptr = NULL;
    ptr = (int*)realloc(SL->data, sizeof(int) * (SL->capable + DEFAULT_INC));
    if (ptr == NULL)
    {
      perror("check:realloc:");
    }
    SL->data = ptr;
    SL->capable += DEFAULT_INC;
    printf("扩容成功\n");
  }
}
void SLPushBack(SeqList* SL,SLDataType x)
{
  assert(SL);
  CheckSL(SL);
  SL->data[SL->sz] = x;
  SL->sz++;
}
void SLPushFront(SeqList* SL, SLDataType x)
{
  assert(SL);
  CheckSL(SL);
  int end = SL->sz - 1;
  while (end >= 0)
  {
    SL->data[end + 1] = SL->data[end];
    end--;
  }
  SL->data[0] = x;
  SL->sz++;
}
void SLPopBack(SeqList* SL)
{
  assert(SL);
  if (SL->sz <= 0)
  {
    printf("表为空,无需删除");
    return;
  }
  SL->sz--;
}
void SLPopFront(SeqList* SL)
{
  assert(SL);
  if (SL->sz <= 0)
  {
    printf("表为空,无需删除");
    return;
  }
  int start = 0;
  while (start < SL->sz - 1)
  {
    SL->data[start] = SL->data[start + 1];
    start++;
  }
  SL->sz--;
}
void SLInsert(SeqList* SL,int pos,SLDataType x)
{
  assert(SL);
  assert(pos >= 0 && pos < SL->sz);
  int end = SL->sz - 1;
  while (end >=pos-1)
  {
    SL->data[end + 1] = SL->data[end];
    end--;
  }
  SL->data[pos-1] = x;
  SL->sz++;
}
void SLEarse(SeqList* SL,int pos)
{
  assert(SL);
  assert(pos >= 0 && pos < SL->sz);
  int start = pos-1;
  while (start < SL->sz - 1)
  {
    SL->data[start] = SL->data[start + 1];
    start++;
  }
  SL->sz--;
}
void SLDestroy(SeqList* SL)
{
  assert(SL);
  free(SL->data);
  SL->data = NULL;
  SL->sz = 0;
  SL->capable = 0;
}
int SLFind(SeqList* SL, SLDataType x)
{
  assert(SL);
  for (int i = 0; i < SL->sz; i++)
  {
    if (SL->data[i] == x)
    {
      return i;
    }
  }
  return -1;
}
void SLModify(SeqList* SL, int pos, SLDataType x)
{
  assert(SL);
  assert(pos >= 0 && pos < SL->sz);
  SL->data[pos - 1] = x;
}

SeqList.h

将主程序所需要的函数全部在头文件中声明,增加代码阅读性

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#define SLDataType int
#define DEFAULT_SZ 5
#define DEFAULT_INC 3
typedef struct SeqList
{
  SLDataType* data;
  int sz;
  int capable;
}SeqList;
void SLInit(SeqList* SL);
void SLprint(SeqList* SL);
void SLPushFront(SeqList* SL, SLDataType x);
void SLPushBack(SeqList* SL, SLDataType x);
void SLPopFront(SeqList* SL);
void SLPopBack(SeqList* SL);
void SLInsert(SeqList* SL, int pos, SLDataType x);
void SLEarse(SeqList* SL, int pos);
int SLFind(SeqList* SL, SLDataType x);
void SLModify(SeqList* SL, int pos, SLDataType x);
void SLDestroy(SeqList* SL);

撒花

这就是实现顺序表的全部内容了,创作不易,还请各位小伙伴多多点赞👍关注收藏⭐,以后也会更新各种关于c语言,数据结构的博客,撒花!



























相关文章
|
1月前
|
存储 编译器 C语言
数据结构-顺序表详解(看这篇就足够了,哈哈哈)
数据结构-顺序表详解(看这篇就足够了,哈哈哈)
51 2
|
20天前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习之顺序表【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找等具体详解步骤以及举例说明
|
20天前
|
存储 C语言
【数据结构】顺序表(c语言实现)(附源码)
本文介绍了线性表和顺序表的基本概念及其实现。线性表是一种有限序列,常见的线性表有顺序表、链表、栈、队列等。顺序表是一种基于连续内存地址存储数据的数据结构,其底层逻辑是数组。文章详细讲解了静态顺序表和动态顺序表的区别,并重点介绍了动态顺序表的实现,包括初始化、销毁、打印、增删查改等操作。最后,文章总结了顺序表的时间复杂度和局限性,并预告了后续关于链表的内容。
51 3
|
20天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之顺序表习题精讲【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找习题精讲等具体详解步骤以及举例说明
|
1月前
|
存储 Java
数据结构第二篇【关于java线性表(顺序表)的基本操作】
数据结构第二篇【关于java线性表(顺序表)的基本操作】
30 6
|
1月前
|
存储 安全 Java
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
23 3
|
1月前
|
存储 C语言
探索C语言数据结构:利用顺序表完成通讯录的实现
本文介绍了如何使用C语言中的顺序表数据结构实现一个简单的通讯录,包括初始化、添加、删除、查找和保存联系人信息的操作,以及自定义结构体用于存储联系人详细信息。
19 2
|
20天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之王道第2.3章节之线性表精题汇总二(5)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
IKU达人之数据结构与算法系列学习×单双链表精题详解、数据结构、C++、排序算法、java 、动态规划 你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
|
存储 算法 索引
【数据结构】——顺序表
【数据结构】——顺序表
|
1月前
|
存储
【数据结构】线性表和顺序表
【数据结构】线性表和顺序表
22 1