当前位置: 首页 > news >正文

【数据结构】顺序表(sequential list)

目录

     一、顺序表的介绍

二、顺序表的实现

1. 静态顺序表

(1)初始化顺序表

(2)建立顺序表

(3)判空操作

(4)求顺序表的长度

(5)遍历操作

(6)增---插入操作

(7)删---按位删除&按值删除

(8)查---按位查找&按值查找

(9)销毁操作

2. 动态顺序表

(1)扩容操作

(2)初始化顺序表

(3)建立顺序表

(4)增---插入操作

(5)销毁操作

三、总代码

1. 静态顺序表

2. 动态顺序表

三、总结


一、顺序表的介绍

        线性表的顺序存储结构称为顺序表(sequential list)。顺序表是用一段地址连续的存储单元依次存储线性表的数据元素的线性结构,一般情况下采用一维数组存储。在数组上完成数据的增删查改。

        而顺序表又分为静态顺序表动态顺序表

二、顺序表的实现

1. 静态顺序表

        静态顺序表是使用固定长度的数组空间来储存数据元素,它的长度大小是不能改变的。

下面是静态顺序表的储存结构定义

#define MaxSize 100            //假设顺序表最多存储100个元素
typedef int DataType;          //定义顺序表的数据类型,假设为int
typedef struct
{DataType data[MaxSize];    //存放数据元素的数组int length;                //线性表的长度
}SeqList;

图示:

其中length和MaxSize的含义是不同的。

(1)初始化顺序表

初始化顺序表,只需将顺序表的长度length初始化为0。

void InitList(SeqList* L)
{L->length = 0;
}

(2)建立顺序表

假设要建立一个长度为n的顺序表,那么就需要将给定数组中的数据元素传入到顺序表中,并将传入的元素个数作为顺序表的长度。假设这里有一个数组a[10] = { 1,2,3,4,5,6,7,8,9,10 },那么就需要判断建立顺序表操作是否成功,如果顺序表的储存空间小于给定的元素个数,则就无法建立。

建立的操作示意图:

则C语言实现如下:

void CreatList(SeqList* L, DataType a[], int n)
{if (n > MaxSize)	//判断顺序表的存储空间是否足够{printf("顺序表的空间不够,无法建立顺序表!\n");return;}for (int i = 0; i < n; i++){L->data[i] = a[i]; }L->length = n;
}

(3)判空操作

顺序表的判空参数只需判断长度length是否为零,如果是空则返回1,如果不是空就返回0。C语言实现如下:

int IsEmpty(SeqList* L)
{if (L->length == 0){return 1; //为空,则返回1}else{return 0; //不为空,则返回0}
}

(4)求顺序表的长度

由于顺序表储存结构定义中的结构体成员length已经保存了顺序表的长度,所以求顺序表长度时只需要返回成员length的值就可以了。C语言实现如下:

int Length(SeqList* L)
{return L->length;
}

(5)遍历操作

遍历操作就是依次输出顺序表中的各个元素。C语言实现如下:

void PrintList(SeqList* L)
{for (int i = 0; i < L->length; i++){printf("%d ", L->data[i]);}
}

(6)增---插入操作

插入操作就是在表的第 i 个位置插入一个新元素 x 的操作。根据不同的插入位置,可以将插入操作分为头插,中间插,尾插三种情况。由于在顺序表中,各个元素的位置都是确定了,他们无法移动。但是当插入一个元素之后,顺序表中的有些元素位置会发生变化。同时他们所对应的下标也会发生变化。就会发生这种情况:当在第 i 个位置插入一个元素后,在 i 这个位置的后面的所有元素的下标都会出现加1的对应情况。换一种说法就是发生了元素的向后移动

移动的操作示意图:

同时还需要判断插入位置的合理性。则C语言实现如下:

void Insert(SeqList* L, int i, DataType x)
{if (L->length >= MaxSize){printf("表满了,插入失败!\n");return 0;}if (i<1 || i>L->length){printf("插入位置不合理,插入失败!\n");return 0;}for (int j = L->length; j >= i; j--){L->data[j] = L->data[j - 1];//后移操作}L->data[i - 1] = x;//插入值L->length++;//长度加一
}

(7)删---按位删除&按值删除

按位删除即将顺序表中的 i 个位置的元素删除。子长度为n的顺序表变成长度为n-1的顺序表。

删除的操作示意图:

当然,这也需要判断删除位置的合理性,则C语言实现如下:

void DeleteByLocate(SeqList* L, int i)
{if (L->length == 0){printf("表空,删除失败!\n");return;}if (i < 1 || i >= L->length){printf("删除位置不合理,删除失败!\n");return;}for (int j = i; j < L->length; j++){L->data[j - 1] = L->data[j];//前移操作}L->length--;//长度减一
}

按值删除需要对顺序表中的元素值进行比较。如果遇到相同的元素时就将该元素删除,即将该元素后面的所有元素都向前移一位。C语言实现如下:

void DeleteByElem(SeqList* L, DataType x)
{int sign = 0;for (int i = 0; i < L->length; i++){if (L->data[i] == x){sign = i;//说明表中有该元素,修改sign,并存记录位置break;}}if (sign != 0){for (int j = sign + 1; j < L->length; j++){L->data[j - 1] = L->data[j];//前移操作}L->length--;//长度减一}else{printf("表中没有元素,删除失败!\n");}
}

(8)查---按位查找&按值查找

按位查找,就是当查找第 i 个元素时,返回 i - 1 这个下标所对应的元素值,这里需要注意,不能直接返回,因为有时会造成歧义,所以需要借助一个指针将对应的元素值存到指针所指向的地址中去。由此来获得该元素值。这就需要查找位置的合理性。如果查找失败,则返回查找失败的标志零,查找成功则返回1。C语言实现如下:

int GetElem(SeqList* L, int i, DataType* p)
{if (i<1 || i>L->length){printf("查找位置不合理,查找失败!\n");return 0;}else{*p = L->data[i - 1];return 1;}
}

按值查找需要对顺序表中的元素值进行依次比较。如果查找成功返回元素的序号(注意这不是下标),否则就返回查找失败的标志0。C语言实现如下:

int GetLocate(SeqList* L, DataType x)
{for (int i = 0; i < L->length; i++){if (L->data[i] == x){return i + 1;//返回序号}}return 0;   //退出循环,说明查找失败
}

(9)销毁操作

        静态顺序表是静态储存分配,在顺序表变量退出作用域时自动释放该变量所在内存单元,因此静态顺序表无需销毁。


2. 动态顺序表

        动态顺序表是通过使用动态开辟的数组存储数据来实现的,它的长度大小是能够改变的。

下面是动态顺序表的储存结构定义

typedef int DataType;       //定义数据类型
typedef struct
{DataType* data;         //指向动态开辟的数组int length;             //有效数据个数int capicity;           //容量空间的大小
}SeqList;

图示:

        与静态顺序表相比,动态顺序表就更加的灵活。静态顺序表只适用于确定知道需要存储多少数据的场景。这会导致静态顺序表的空间开多了浪费,开少了又不够用。但动态顺序表则会根据需要的场景来动态的分配空间大小。

其中静态顺序表和动态顺序表他们的代码实现大部分是相同的。即判空操作,求长度操作,遍历操作,删除操作,查找操作这几种操作和静态的顺序表的操作都是一模一样的

  • 其中不同的是动态顺序表在初始化时需要动态开辟空间;
  • 在进行建立和插入的操作时,需要判断当下空间大小是否足够。如果不够就需要增容;
  • 再就是动态顺序表的销毁操作,由于动态顺序表示动态申请的空间,所以在销毁时就需要全部释放。
  • 动态顺序表必须要有销毁操作。

下面介绍的便是与静态顺序表不同的操作。

(1)扩容操作

简单的扩容操作就是将容量扩大为原来的两倍即可。首先是判断当有效数据个数等于容量空间大小时就需要扩容。扩容需要将容量开辟为原来的两倍,而这里这里使用relloc来开辟空间更加方便。C语言实现如下:

void IncreaseCapacity(SeqList* L)
{if (L->length == L->capicity){DataType* tmp = (DataType*)realloc(L->data, sizeof(DataType) * L->capicity * 2);if (tmp == NULL){perror("relloc fail");return;}	L->data = tmp;L->capicity *= 2;}
}

(2)初始化顺序表

由于需要动态开辟空间,所以可以在开辟空间之前断言传递的指针L非空,再用malloc开辟空间。然后需要将有效数据个数字置零,并设置初始容量空间大小。C语言实现如下:

void InitList(SeqList* L)
{assert(L);//断言L非空指针L->data = (DataType*)malloc(sizeof(DataType) * InitCapicity);//开辟初始空间if (L->data == NULL){perror("malloc fail");return;}L->length = 0;//有效数据个数置零L->capicity = InitCapicity;//设置初始容量
}

(3)建立顺序表

建立动态存储的顺序表示的逻辑和建立静态顺序表时的逻辑基本上是一样的。其中不同的是当我们在将一个数组中的数据赋值到动态顺序表中时,也许会遇到所需要赋值的数据个数大于此时的容量的情况,这时候就需要扩容。因此只需要在建立顺序表的函数的前一个语句运行扩容操作的函数就行了。C语言实现如下:

void CreatList(SeqList* L, DataType a[], int n)
{for (int i = 0; i < n; i++){IncreaseCapacity(L);//扩容操作L->data[i] = a[i];L->length++;//每加一个数据,有效数据个数就加一}
}

(4)增---插入操作

插入操作即在第 i 个位置插入值 x 的数据 ,大部分和静态顺序表的操作是一样的,不同的是动态顺序表没有表满就不能插入的插入失败情况了,并且需要检查空间,如果容量空间被占满了,就需要扩大容量。C语言实现如下:

void Insert(SeqList* L, int i, DataType x)
{if (i < 1 || i > L->length){printf("插入位置不合理,插入失败!\n");return;}IncreaseCapacity(L);for (int j = L->length; j >= i; j--){L->data[j] = L->data[j - 1];//后移操作}L->data[i - 1] = x;//插入值L->length++;//有效个数加一
}

 当然,由此可以引申出头插尾插的操作了。

(5)销毁操作

销毁操作需要将动态申请的所有空间都释放。并且其余变量都置为零。C语言实现如下:

void Destory(SeqList* L)
{free(L->data);L->data = NULL;L->length = 0;L->capicity = 0;
}

三、总代码

1. 静态顺序表

头文件:SeqList.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 100            //假设顺序表最多存储100个元素
typedef int DataType;          //定义数据类型
typedef struct
{DataType data[MaxSize];    //存放数据元素的数组int length;                //线性表的长度
}SeqList;void InitList(SeqList* L);//初始化void CreatList(SeqList* L, DataType a[], int n);//建立顺序表int IsEmpty(SeqList* L);//判空操作int Length(SeqList* L);//求顺序表的长度void PrintList(SeqList* L);//遍历操作void Insert(SeqList* L, int i, DataType x);//插入操作void DeleteByLocate(SeqList* L, int i);//按位删除void DeleteByElem(SeqList* L, DataType x);//按值删除int GetLocate(SeqList* L, DataType x);//按值查找int GetElem(SeqList* L, int i, DataType* p);//按位查找

源文件:SeqList.c

#define  _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"
void InitList(SeqList* L)
{L->length = 0;
}void CreatList(SeqList* L, DataType a[], int n)
{if (n > MaxSize)	//判断顺序表的存储空间是否足够{printf("顺序表的空间不够,无法建立顺序表!\n");return;}for (int i = 0; i < n; i++){L->data[i] = a[i];}L->length = n;
}int IsEmpty(SeqList* L)
{if (L->length == 0){return 1; //为空,则返回1}else{return 0; //不为空,则返回0}
}int Length(SeqList* L)
{return L->length;
}void PrintList(SeqList* L)
{for (int i = 0; i < L->length; i++){printf("%d ", L->data[i]);}
}void Insert(SeqList* L, int i, DataType x)
{if (L->length >= MaxSize){printf("表满了,插入失败!\n");return;}if (i < 1 || i > L->length){printf("插入位置不合理,插入失败!\n");return;}for (int j = L->length; j >= i; j--){L->data[j] = L->data[j - 1];//后移操作}L->data[i - 1] = x;//插入值L->length++;//长度加一
}void DeleteByLocate(SeqList* L, int i)
{if (L->length == 0){printf("表空,删除失败!\n");return;}if (i < 1 || i >= L->length){printf("删除位置不合理,删除失败!\n");return;}for (int j = i; j < L->length; j++){L->data[j - 1] = L->data[j];//前移操作}L->length--;//长度减一
}void DeleteByElem(SeqList* L, DataType x)
{int sign = 0;for (int i = 0; i < L->length; i++){if (L->data[i] == x){sign = i + 1;//说明表中有该元素,修改sign,并存记录位置break;}}if (sign != 0){for (int j = sign; j < L->length; j++){L->data[j - 1] = L->data[j];//前移操作}L->length--;//长度减一}else{printf("表中没有元素,删除失败!\n");}
}int GetLocate(SeqList* L, DataType x)
{for (int i = 0; i < L->length; i++){if (L->data[i] == x){return i + 1;//返回序号}}printf("表中没有该元素,查找失败!\n");return 0;   //退出循环,说明查找失败
}int GetElem(SeqList* L, int i, DataType* p)
{if (i < 1 || i > L->length){printf("查找位置不合理,查找失败!\n");return 0;}else{*p = L->data[i - 1];return 1;}
}

源文件:test.c

#define  _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"
int main()
{int a[] = { 10,12,15,25,8,16,20 };int sz = sizeof(a) / sizeof(a[0]);SeqList L;InitList(&L);CreatList(&L, a, sz);if (IsEmpty(&L)){printf("该顺序表为空\n");}printf("***********构造顺序表并遍历打印***********\n");PrintList(&L);printf("\n该顺序表的长度为%d\n", Length(&L));int i, x;printf("\n**************增---插入操作***************\n");printf("请输入要插入的位置和要插入的值:");scanf("%d%d", &i, &x);Insert(&L, i, x);PrintList(&L);printf("\n该顺序表的长度为%d\n", Length(&L));printf("\n**************删---按位删除***************\n");printf("请输入要删除的位置:");scanf("%d", &i);DeleteByLocate(&L, i);PrintList(&L);printf("\n\n**************删---按值删除***************\n");printf("请输入要删除的元素值:");scanf("%d", &x);DeleteByElem(&L, x);PrintList(&L);printf("\n\n**************查---按值查找***************\n");printf("请输入要查找的元素值:");scanf("%d", &x);int ret = GetLocate(&L, x);if(ret != 0){printf("元素%d的位置是%d\n", x, ret);}printf("\n**************查---按位查找***************\n");printf("请输入要查找的元素位置:");scanf("%d", &i);if (GetElem(&L, i, &x) != 0){printf("第%d个位置的元素是%d\n", i, x);}return 0;
}

运行结果:

2. 动态顺序表

头文件:SeqList.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define InitCapicity 4      //设置初始容量
typedef int DataType;       //定义数据类型
typedef struct
{DataType* data;         //指向动态开辟的数组int length;             //有效数据个数int capicity;           //容量空间的大小
}SeqList;void IncreaseCapacity(SeqList* L);//扩容操作void InitList(SeqList* L);//初始化void CreatList(SeqList* L, DataType a[], int n);//建立顺序表int IsEmpty(SeqList* L);//判空操作int Length(SeqList* L);//求顺序表的长度void PrintList(SeqList* L);//遍历操作void Insert(SeqList* L, int i, DataType x);//插入操作void DeleteByLocate(SeqList* L, int i);//按位删除void DeleteByElem(SeqList* L, DataType x);//按值删除int GetLocate(SeqList* L, DataType x);//按值查找int GetElem(SeqList* L, int i, DataType* p);//按位查找void Destory(SeqList* L);//销毁操作

源文件:SeqList.c

#define  _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"
void IncreaseCapacity(SeqList* L)
{if (L->length == L->capicity){DataType* tmp = (DataType*)realloc(L->data, sizeof(DataType) * L->capicity * 2);if (tmp == NULL){perror("relloc fail");return;}	L->data = tmp;L->capicity *= 2;printf("扩容成功!\n");}
}void InitList(SeqList* L)
{assert(L);//断言L非空指针L->data = (DataType*)malloc(sizeof(DataType) * InitCapicity);//开辟初始空间if (L->data == NULL){perror("malloc fail");return;}L->length = 0;//有效数据个数置零L->capicity = InitCapicity;//设置初始容量
}void CreatList(SeqList* L, DataType a[], int n)
{for (int i = 0; i < n; i++){IncreaseCapacity(L);//扩容操作L->data[i] = a[i];L->length++;//每加一个数据,有效数据个数就加一}
}int IsEmpty(SeqList* L)
{if (L->length == 0){return 1; //为空,则返回1}else{return 0; //不为空,则返回0}
}int Length(SeqList* L)
{return L->length;
}void PrintList(SeqList* L)
{for (int i = 0; i < L->length; i++){printf("%d ", L->data[i]);}
}void Insert(SeqList* L, int i, DataType x)
{if (i < 1 || i > L->length){printf("插入位置不合理,插入失败!\n");return;}IncreaseCapacity(L);for (int j = L->length; j >= i; j--){L->data[j] = L->data[j - 1];//后移操作}L->data[i - 1] = x;//插入值L->length++;//长度加一
}void DeleteByLocate(SeqList* L, int i)
{if (L->length == 0){printf("表空,删除失败!\n");return;}if (i < 1 || i >= L->length){printf("删除位置不合理,删除失败!\n");return;}for (int j = i; j < L->length; j++){L->data[j - 1] = L->data[j];//前移操作}L->length--;//长度减一
}void DeleteByElem(SeqList* L, DataType x)
{int sign = 0;for (int i = 0; i < L->length; i++){if (L->data[i] == x){sign = i + 1;//说明表中有该元素,修改sign,并存记录位置break;}}if (sign != 0){for (int j = sign; j < L->length; j++){L->data[j - 1] = L->data[j];//前移操作}L->length--;//长度减一}else{printf("表中没有元素,删除失败!\n");}
}int GetLocate(SeqList* L, DataType x)
{for (int i = 0; i < L->length; i++){if (L->data[i] == x){return i + 1;//返回序号}}printf("表中没有该元素,查找失败!\n");return 0;   //退出循环,说明查找失败
}int GetElem(SeqList* L, int i, DataType* p)
{if (i < 1 || i > L->length){printf("查找位置不合理,查找失败!\n");return 0;}else{*p = L->data[i - 1];return 1;}
}void Destory(SeqList* L)
{free(L->data);L->data = NULL;L->length = 0;L->capicity = 0;
}

源文件:test.c

#define  _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"
int main()
{int a[] = { 10,12,15,25,8,16,20 };int sz = sizeof(a) / sizeof(a[0]);SeqList L;InitList(&L);printf("***********构造顺序表并遍历打印***********\n");CreatList(&L, a, sz);if (IsEmpty(&L)){printf("该顺序表为空\n");}PrintList(&L);printf("\n该顺序表的长度为%d\n", Length(&L));int i, x;printf("\n**************增---插入操作***************\n");printf("请输入要插入的位置和要插入的值:");scanf("%d%d", &i, &x);Insert(&L, i, x);PrintList(&L);printf("\n该顺序表的长度为%d\n", Length(&L));printf("\n**************删---按位删除***************\n");printf("请输入要删除的位置:");scanf("%d", &i);DeleteByLocate(&L, i);PrintList(&L);printf("\n\n**************删---按值删除***************\n");printf("请输入要删除的元素值:");scanf("%d", &x);DeleteByElem(&L, x);PrintList(&L);printf("\n\n**************查---按值查找***************\n");printf("请输入要查找的元素值:");scanf("%d", &x);int ret = GetLocate(&L, x);if (ret != 0){printf("元素%d的位置是%d\n", x, ret);}printf("\n**************查---按位查找***************\n");printf("请输入要查找的元素位置:");scanf("%d", &i);if (GetElem(&L, i, &x) != 0){printf("第%d个位置的元素是%d\n", i, x);}Destory(&L);return 0;
}

四、总结

        静态顺序表与动态顺序表作为线性表中顺序表的两种存储结构,均以连续的内存存储空间实现数据的访问,但也存在许多差异。

  1. 静态顺序表编译时是确定的固定容量,具有内存分配简单,但空间利用率低且无法动态扩展,适用于数据规模已知且稳定的场景;
  2. 动态顺序表则通过动态内存分配在运行时按需调整表的大小,更适合数据规模变化频繁或难以预估的场景。

http://www.dtcms.com/a/272315.html

相关文章:

  • 学习中断配置的一天(第五天)
  • 安装nginx+php环境
  • OpenCV探索之旅:多尺度视觉与形状的灵魂--图像金字塔与轮廓分析
  • 无人机识别比赛记录与分析
  • Java---IDEA
  • 【论文阅读】Decoupled Knowledge Distillation
  • 【大模型推理论文阅读】 Thinking Tokens are Information Peaks in LLM Reasoning
  • iOS 抓包详细教程:从零搭建、操作到实战调试的全流程指南
  • 图像亮度调整的简单实现
  • Flutter多线程机制深度解析
  • 【Docker基础】Docker容器与网络关联命令使用指南:深入理解容器网络连接
  • 力扣61.旋转链表
  • Windows下VScode配置FFmpeg开发环境保姆级教程
  • 面试150 LRU缓存
  • LeetCode - 1668. 最大重复子字符串
  • 原创:多面体编译,polybench-c-4.2批量测试脚本
  • php中array($this, ‘loadClass‘)表示啥意思?
  • 阿里云-跨账号同步OSS Bucket
  • 【Note】Linux Kernel 之 内核架构、源码文件、API/ABI 、FHS
  • Linux 内核日志中常见错误
  • idea安装maven 拉取依赖失败的解决办法
  • 网络基本概念
  • Ubuntu22.0.4安装PaddleNLP
  • Android Coil 3 data加载图的Bitmap或ByteArray数据类型,Kotlin
  • Redis BigKey 深度解析:从原理到实战解决方案
  • arm架构,arm内核,处理器之间的关系
  • 【部分省份已考真题】备战2025全国青少年信息素养大赛-算法创意实践挑战赛c++省赛/复赛真题——被污染的药剂
  • 如何发现Redis中的bigkey?
  • Django中序列化与反序列化
  • Python(31)PyPy生成器优化深度解析:JIT加速下的Python性能革命