顺序表的插入和删除

article/2025/8/22 3:36:47

前言

相信通过上一篇文章(顺序表的定义)大家已经能动手定义一个顺序表,并且知道顺序表如何进行初始化的工作。当完成一个顺序表的建立和初始化后,我们得到的会是一个空的顺序表(空表),所以这篇文章我们来学习顺序表的两个基本操作——插入和删除。

顺序表的基本操作——插入

ListInsert(&L,i,e):插入操作。在表L中的第i个位置(位序)上插入指定元素e

  • 位序的意思就是按照我们正常生活中的理解,第1位就是第1个,而不是程序语言中的第0位代表第1个

现假设我们用静态分配的方式实现了一个顺序表:

#define MaxSize 10				// 定义最大长度
typedef struct{ElemType data[MaxSize];		// 用静态的“数组”存放数据元素int length;					// 顺序表的当前长度
}SqList;						// 顺序表的类型定义

那么在内存中我们看到的应该是这样。

image-20220705093627056

假设现在我们已经存入了5个元素,其在顺序表中存放应该是这样。

image-20220705093721667

如果这时候我们要进行插入操作——在第3个位置插入一个元素c。

image-20220705093849561

这时候c就应该成为b的后继节点,d的前驱节点。

image-20220705093946228

由于顺序表是通过物理地址的相邻来表示逻辑相邻的元素。

image-20220705094143579

所以这时候就需要将原先处于第3个位置及第3个位置以后的所有元素都后移1位,再将c插入到第3个位置。

image-20220705094111179

广义化

从上述的例子中我们可以得出一个普遍的结论:

  • 如果想在第i个位置插入n个元素,那么就要将第i个元素及第i个元素以后的所有元素,向后移n位,然后再以此插入这n个元素

代码实现

注意:我们本篇文章主要以静态分配的方式来实现,当然动态分配也大同小异

在这里我们只书写了部分代码

#define MaxSize 10				// 定义最大长度
typedef struct{int data[MaxSize];		// 用静态的“数组”存放数据元素int length;					// 顺序表的当前长度
}SqList;						// 顺序表的类型定义void ListInsert(SqList &L,int i,int e){for(int j=L.length;j>=i;j--)        // 将第i个元素及之后的元素后移L.data[j] = L.data[j-1];L.data[i-1] = e;                    // 在位置i处插入e元素L.length++;                         // 长度+1
}int main(){SqList L;              // 声明顺序表InitList(L);            // 初始化顺序表// ...省略往顺序表插入元素的过程(你可以假装它满了)ListInsert(L,3,3)return 0;
}

代码分析

声明顺序表和初始化顺序表在上篇文章中已经做了详细的介绍,这里就不再过多介绍,我们直接开始介绍ListInsert()插入函数的操作

  1. 开头的for循环是顺序表的最后端(最后一个元素)开始将元素后移
    • 令j = 顺序表的最后一个元素,每一次移动后j都-1,并且在j<i之后停止循环
      • 这里我们用上面的假设:顺序表里已经有5个元素,要在第3个位置插入一个3(ListInsert(L,3,3):在表L中的第3个位置插入一个元素3)
      • j = 5(但是我们知道在程序语言中,下标是5相当于表示的是第6个位置)
      • L.data[j] = L.data[j-1]:将顺序表中第5个位置的元素赋值给第6个元素(就相当于把第5个位置后移至第6个位置)
      • 以此类推,实现顺序表元素后移的操作。当j=i时,这里就是j=3时,L.data[j] = L.data[j-1]:将第3个位置的元素赋值给第4个位置。到此第3个位置的元素以及第3个位置往后的元素已经全部后移一位
  2. 经过上面的循环后移操作后,第3个位置已经空出,我们直接在第3个位置上插入我们要插入的元素e(赋值为3),上面我们说过由于在程序语言中,是从0开始,所以i=3时表示的是数组中第4个位置,所以要i-1
    • 注意这里的虽然第3个位置经过循环后移操作已经空出,但是不代表里面没有数据,这里只是将前一位赋值到后一位,但是前一位的数值仍然存在。当你进行了赋值操作之后,覆盖了原来的数据,才算真正的数据消失(这里听不懂没关系,可能我说的比较抽象,只是为了更加的严谨)
  3. 由于插入了一个新元素,所以顺序表的长度要+1

问题

这时候已经可以让别人调用自己写的函数实现顺序表的插入功能了,但是我们的代码还是比较的薄弱,仍然还有一些问题,就是可能会存在一些不合法的操作。

image-20220705104014313

如果此时顺序表中只有6个元素,而你的猪队友想在第9个位置插入元素呢?这显然时不合法的,因为我们知道顺序表是通过物理地址的相邻来表示逻辑位置的相邻,第9个位置插入元素,但是第7和第8的位置上都还没有元素。

这时我们就需要进行插入操作的范围限定。如果我们已经有了6个元素,那么我们可以操作的范围应该是:[1,7](注意这里是使用位序表达, 即1代表第1个位置),也就是[1,length+1]。如果传入的i超出了这个范围,那么我们就不能继续进行下面的操作,所以可以加入一个判断,来判断i是否处于可操作的范围内。

当然,如果当你的顺序表已经插满时,我们也不能在进行插入,所以我们还应该加入一段代码用来检测顺序表是否已经存满,如果已经从存满则不进行下面插入的操作。

反馈

虽然说上面我们考虑到了一些不合法的操作,并且插入了代码检测并且限制。但是不管是哪种不合法的情况,我们都应该要给使用者一些反馈,这样才能让他们知道他们到底是什么操作导致了插入不成功。

所以说我们在写代码的时候除了要保障代码的逻辑正确,此外还要让别人在使用我们代码的时候比较的舒服。比如我们可以在他们可能产生不合法行为的地方返回一些东西,合法行为的地方也返回一些东西。

bool ListInsert(SqList &L,int i,int e){if(i<1 || i>L.length+1)			// 判断i的范围是否有效return false;if(L.length>=MaxSize)			// 当前存储空间已满,不能插入return false;for(int j=L.length;j>=i;j--)	//	将第i个元素及以后的元素后移L.data[j] = L.data[j-1];L.data[i-1] = e;				// 在第i个位置插入eL.length++;						// 长度+1return true;
}

从以上的代码我们若检测到用户的不合法操作,则返回一个false的布尔值,以告诉使用者你的操作不合法。而在合法执行操作后返回一个true的布尔值来告诉使用者,你的操作合法。按照上述这样的代码才具有”健壮性“

插入操作的时间复杂度

在计算时间复杂度的时候,我们应该是关注最深层循环的执行次数与问题规模n的关系。在这里最深层的循环时将元素后移的语句

for(for(int j=L.length;j>=i;j--))L.data[j] = L.data[j-1];

我们在之前的时间复杂度中说到过如果碰到问题规模相同会有(最好、最坏、平均)的情况:

  • 最好情况:新元素插入到表尾,不需要移动元素
    • i = n+1 ,循环次数0,最好时间复杂度 = O(1)
  • 最坏情况:新元素插入表头,需要将原有的n个元素全部向后移动
    • i = 1,循环次数n+1次,最坏时间复杂度 = O(n)
  • 平均情况:假设新元素插入到任何一个位置的概率相同,所以 i = 1,2,3,…,length+1的概率 p = 1 n + 1 p=\frac{1}{n+1} p=n+11
    • n+1是因为原来有n个元素,但是要插入一个新的元素,所有应该有n+1个元素
    • 插入的元素可以插入n个位置中的任意一个位置,也可以插入到表尾(n+1)的位置
    • 当i = 1时,循环n次;i = 2时,循环n-1次… i = n+1时,循环0次。故平均循环次数为 n p + ( n − 1 ) p + ( n − 2 ) p + . . . + 1 p = n ( n + 1 ) 2 1 n + 1 = n 2 np+(n-1)p+(n-2)p+...+1p=\frac{n(n+1)}{2}\frac{1}{n+1}=\frac{n}{2} np+(n1)p+(n2)p+...+1p=2n(n+1)n+11=2n,平均时间复杂度: O ( n 2 ) − > O ( n ) O(\frac{n}{2})->O(n) O(2n)>O(n)

在这里我们可以看到,问题规模为n时,执行的次数为n,所以插入操作的时间复杂度为:O(n)

顺序表的基本操作——删除

经过上面的插入学习如果你理解了,其实对于删除操作也就知道的差不多了。

删除的操作则是要将需要删除的位置的元素删除,并且将该位置之后的所有元素前移。

举例

我们要删除c元素

image-20220705112156732

删除c元素

image-20220705112230988

并且将原先c后面的所有元素前移一位

image-20220705112314028

同时要把length的值-1

代码实现

bool ListDelete(SqList &L,int i,int &e){if(i<1 || i>L.length)               // 判断i的范围是否有效return false;e = L.data[i-1];                    // 将被删除的元素赋值给efor(int j=i;j<L.length;j++)         // 将第i个位置的元素前移L.data[j-1] = L.data[j];L.length--;return true;
}int main(){SqList L;               // 声明顺序表InitList(L);            // 初始化顺序表// ...省略往顺序表插入元素的过程(你可以假装它满了)int e = -1              // 用变量e把删除的元素”带回来“if(ListDelete(L,3,e))printf("已删除第3个元素,删除元素值为=%d\n",e);elseprintf("位序i不合法,删除失败\n");return 0;
}

ListDelete(SqList &L,int i,int &e)

  • &L:要操作的顺序表
  • i:要删除的位置
  • &e:将删除的值赋值,并且”带回“

代码分析

  1. main函数中定义了一个int e用来存储删除的元素的值
  2. 上面我们分析了删除操作的函数参数,e使用了引用,是为了将删除的值带回
  3. 进入ListDelete()函数后,首先对i的合法性进行了一个判断,若不合法则返回false
    • 因为我们要删除的元素肯定是已经有了的元素,所以最大值不能超过length
  4. 将马上要删除的元素先赋值给e,让e带回
  5. for循环,将第i个位置(要删除的元素)之后的所有元素前移一位
  6. 由于是删除操作,元素的数量会减少,顺序表的长度也会减少,所以长度-1
  7. 操作完成后,返回true

注意:当我们进行插入操作的时候是从后往前,先把后面的元素后移,再后前面的元素。而再删除操作中是从前往后,先将前面的元素前移,再前移后面的元素。

删除操作的时间复杂度

与插入操作相同,删除操作由于是对数组进行操作,故会有(最好情况、最坏情况和平均情况)。

  • 最好情况:删除表尾元素,不需要移动其他元素。
    • i = n,循环0次。时间复杂度 = O(1)
  • 最坏情况:删除表头元素,需要将后面的n-1个元素全部向前移动。
    • i = 1,循环n-1次,最坏时间复杂度 = O(n)
  • 平均情况:假设删除任意一个元素的概率相同,即i = 1,2,3…length的概率都是 p = 1 n p=\frac{1}{n} p=n1
    • 由于删除的元素只有n个,所以概率应为 1 n \frac{1}{n} n1
    • i = 1,循环n-1次,i = 2,循环n-2次… i = n时,循环0次
    • 平均循环次数 = ( n − 1 ) p + ( n − 2 ) p + . . . + 1 p = n ( n − 1 ) 2 1 n = n − 1 2 (n-1)p+(n-2)p+...+1p=\frac{n(n-1)}{2}\frac{1}{n}=\frac{n-1}{2} (n1)p+(n2)p+...+1p=2n(n1)n1=2n1,平均时间复杂度 = O ( n − 1 2 ) − > O ( n ) O(\frac{n-1}{2})->O(n) O(2n1)>O(n)

结束语

已同步更新至个人博客:https://www.hibugs.net/index.php/sqlistinsndel/

本人菜鸟一枚,仅分享学习笔记和经验。若有错误欢迎指出!共同学习、共同进步 😃

如果您觉得我的文章对您有所帮助,希望可以点个赞和关注,支持一下!十分感谢~(若您不想也没关系,只要文章能够对您有所帮助就是我最大的动力!)

下一篇文章传送门:顺序表的查找


http://chatgpt.dhexx.cn/article/mk05tspB.shtml

相关文章

数组和顺序表的区别

前言 看到很多人直接将顺序表等同于数组&#xff0c;认为顺序表就是数组&#xff0c;但这样做容易造成概念混淆。 下面就对这两个概念进行解释&#xff0c;帮助大家进行区分。 什么是顺序表 在解释什么是顺序表之前&#xff0c;我们还需要了解一点基础知识。 数据结构 数据…

数据结构之顺序表:顺序表的结构及基本操作

目录 一、数据结构1.1 算法与数据结构的区别 二、顺序表2.1 顺序表的基本形式【重点】2.2 顺序表的两种基本实现方式【重点】1、一体式结构&#xff1a;2、分离式结构: 2.3 元素存储区替换与扩充1. 元素存储区的替换2. 元素存储区的扩充 2.4 顺序表的操作1. 增加元素2. 删除元素…

简洁顺序表

目录 前言 一、初始准备 二、尾插尾删 三、头插尾删 四、随机位置插入删除 五、顺序表缺陷 六、全部代码 前言 顺序表和链表都是线性表 顺序表的本质就是数组&#xff0c;能够连续存储数据 一、初始准备 建立结构体 静态版本 由于静态版本容量是固定的&#xff0c…

~~顺序表~~

1.线性结构的特点是&#xff1a; 在数据元素的非空有限集中&#xff1a; (1).存在唯一的一个被称为“第一个”的数据元素 (2).存在唯一的一个被称为“最后一个”的数据元素 (3).除第一个之外&#xff0c;集合中的每个数据元素都只有一个前驱 (4).除第一个之外&#xff0c;…

顺序表的定义

1.顺序表的定义 顺序表——用顺序存储的方式实现线性表顺序存储 eg: A1-A2-A3-A4-A5 如果第一个位置是location(L)&#xff0c;那么第二个就是location(L)数据元素大小 [sizeof(ElemType)可以查看数据元素大小] 2.顺序表的实现——静态分配 #define MaxSize 10 //定义最大长…

C语言实现顺序表

c语言实现顺序表 线性表是最简单的数据结构&#xff0c;而顺序表又是最简单的线性表&#xff0c;其基本思想是用一段地址连续的储存单元依次存储线性表的数据元素&#xff0c;比如我们常用的一维数组&#xff0c;下面代码实现了顺序表的定义以及基本操作。 编译环境&#xff…

顺序表的实现

目录 1.顺序表的概念 2.静态顺序表 分析&#xff1a; 3.动态顺序表 分析&#xff1a; 4.顺序表初始化 5.顺序表尾部操作 5.1尾插 空间检查函数实现 分析&#xff1a; 5.2尾删 分析&#xff1a; 6.顺序表的头部操作 6.1头插 分析&#xff1a; 6.2头删 分析&…

【C语言】顺序表的创建

一、代码实现部分&#xff1a; 1、顺序表是线性表的基础部分&#xff0c;至于顺序表&#xff0c;在本人看来无异于数组。至于线性表的概念&#xff0c;在此不再赘述。接下来尝试利用C语言对线性表中的顺序表进行代码实现&#xff08;此程序中规定用户输入的数据类型为int类型&a…

顺序表和链表

1.今天给大家介绍线性表中两个常见的结构顺序表和链表&#xff0c;其中链表又包括单链表和带头双向循环链表。 2.此部分的全部代码放在个人gitee中 &#xff0c;需要的自行拿取&#xff0c;前后文件依次对应SeqList SList DList。gitee链接点这里 一、线性表 1.线性表 线性表&…

顺序表的增删查改

数据结构 是数据存储的方式&#xff0c;对于不同的数据我们要采用不同的数据结构。就像交通运输&#xff0c;选用什么交通工具取决于你要运输的是人还是货物&#xff0c;以及它们的数量。 顺序存储结构 包括顺序表、链表、栈和队列等。 例如腾讯QQ中的好友列表&#xff0c;…

顺序表初始化

文章目录 1. 顺序表2. 顺序表的初始化 1. 顺序表 顺序表(顺序存储结构) 存储数据时&#xff0c;会提前申请一整块足够大小的物理空间&#xff0c;然后将数据依次存储到一整块连续的存储空间内&#xff0c;存储时做到数据元素之间不留一丝缝隙。 使用顺序表存储集合 {1,2,3,4,…

顺序表的创建

三个朋友今天全部上岸大厂&#xff0c;祝贺。&#xff08;太羡慕了&#xff09; 静态分配创建一个顺序表&#xff1b; 1.顺序表的定义&#xff1a; #define MaxSize 10 typedef struct {ElemType data[MaxSize];int length; }SqlList;这里我们用结构体的方式定义以一个顺序表…

顺序表的详解

线性表是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串.... 即顺序表为线性表的一种&#xff0c; 顺序表是一种物理地址连续的存储单元依次存储数据元素的线性结构&#…

什么是顺序表

顺序表 在程序中&#xff0c;经常需要将一组&#xff08;通常是同为某个类型的&#xff09;数据元素作为整体管理和使用&#xff0c;需要创建这种元素组&#xff0c;用变量记录它们&#xff0c;传进传出函数等。一组数据中包含的元素个数可能发生变化&#xff08;可以增加或删…

数据结构——顺序表

目录 一. 什么是顺序表&#xff1f; 1&#xff0c;静态顺序表 2&#xff0c;动态顺序表 ①动态顺序表的实现及其初始化 ②空间的创建 ③顺序表的打印和销毁 ④顺序表的尾部插入和删除 ⑤顺序表的头部插入和删除 ⑥顺序表pos位置的插入和删除 ⑦顺序表指定元素的删除 二&am…

【数据结构】——顺序表介绍(独家介绍,小白必看!!)

重点和易错点都用彩笔标记出来了&#xff0c;放心食用&#xff01;&#xff01; 数据结构分为线性表和非线性表&#xff0c;今天我们要学习的顺序表就是线性表中的一个小类。那么&#xff0c;何为线性表&#xff0c;线性表是指n个具有相同性质的数据元素的有限序列&#xff0c…

顺序表详解

目录 一、线性表的概念 二、顺序表 1、顺序表的概念 1&#xff09;. 静态顺序表&#xff1a;使用定长数组存储元素。 2&#xff09;. 动态顺序表&#xff1a;使用动态开辟的数组存储。 三、接口实现 1、创建 2、初始化 3、扩容 4、尾插 5、打印 6、销毁 7、尾删 越界&…

php的strstr是什么意思,php strstr函数怎么用

strstr()函数是PHP中的一个内置函数&#xff0c;语法为strstr(string,search,before_search) &#xff0c;用于搜索字符串在另一字符串中是否存在&#xff0c;如果是&#xff0c;返回该字符串及剩余部分&#xff0c;否则返回 FALSE。此函数区分大小写。 php strstr()函数怎么用…

【数据结构】--- kmp算法和strstr函数

kmp算法和strstr函数 引言一、概念分析分析原理分析 KMP算法原理基本操作图解KMP原理 三、复杂度分析四、KMP算法代码 引言 现实生活中&#xff0c;字符串匹配在很多的应用场景里都有着极其重要的作用&#xff0c;包括生物信息学、信息检索、拼写检查、语言翻译、数据压缩、网…

实现strstr函数

strstr函数的作用是寻找子字符串在目标字符串中第一次出现的位置。 #include <stdio.h> #include<stdlib.h> #include<assert.h> const char * Strstr(const char * str1, const char * str2) {assert(str1 ! NULL);assert(str2 ! NULL);char* cur str1;ch…