数据结构 c语言版 ——顺序表的查找、插入与删除
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构 c语言版 ——顺序表的查找、插入与删除相关的知识,希望对你有一定的参考价值。
1. 实验项目的目的和任务
顺序表的查找、插入与删除。设计算法,实现线性结构上的顺序表的产生以及元素的查找、插入与删除。具体实现要求:
1) 从键盘输入10个整数,产生顺序表,并输入结点值。
2) 从键盘输入1个整数,在顺序表中查找该结点的位置。若找到,输出结点的位置;若找不到,则显示“找不到”。
3) 从键盘输入2个整数,一个表示欲插入的位置i,另一个表示欲插入的数值x,将x插入在对应位置上,输出顺序表所有结点值,观察输出结果。
4) 从键盘输入1个整数,表示欲删除结点的位置,输出顺序表所有结点值,观察输出结果。
2. 上机实验内容
1. 定义一个整型数组。用于存放顺序表的数据元素
2. 设计一个函数,完成顺序表的建立。
从键盘输入10个整数,产生顺序表,并输入结点值。
3. 设计一个函数,完成顺序表的查找。
从键盘输入1个整数,在顺序表中查找该结点的位置。若找到,输出结点的位置;若找不到,则显示“找不到”。
4. 设计一个函数,完成顺序表的插入。
从键盘输入2个整数,一个表示欲插入的位置i,另一个表示欲插入的数值x,将x插入在对应位置上,输出顺序表所有结点值,观察输出结果。
5. 设计一个函数,完成顺序表的删除。
从键盘输入1个整数,表示欲删除结点的位置,输出顺序表所有结点值,观察输出结果。
6. 设计一个函数,用于顺序显示当前线性表中的所以元素。
3. 主要实验方法
程序主框架已经设计好。见SqList.C文件。请按要求设计各个函数,并完成正确调用。
下面是SqList.C里的内容
#include<stdio.h>
#include<stdlib.h>
#define N 10 //顺序表的最大容量
int length=0; //顺序表的当前元素个数
void main()
int List[N];
char ch,exit='N';
do
system("CLS");
printf("\t\t********************************************\n");
printf("\t\t* 1.创建一个顺序表 .........(1) *\n");
printf("\t\t* 2.在顺序表中查找元表.........(2) *\n");
printf("\t\t* 3.在顺序表中插入元表.........(3) *\n");
printf("\t\t* 4.在顺序表中删除元表.........(4) *\n");
printf("\t\t* 5.退出 .........(5) *\n");
printf("\t\t********************************************\n");
printf("\n请选择操作代码:");
ch=getchar();
getchar();
switch(ch)
case '1':
//请在这里调用创建顺序表的函数,并删除printf函数
printf("创建一个顺序表\n");
break;
case '2':
//请在这里调用查找元素的函数,并删除printf函数
printf("在顺序表中查找元表\n");
break;
case '3':
//请在这里调用插入元素的函数,并删除printf函数
printf("在顺序表中插入元表\n");
break;
case '4':
//请在这里调用删除元素的函数,并删除printf函数
printf("在顺序表中删除元表\n");
break;
case '5':
printf("\n您是否真的要退出程序(Y/N):");
exit=getchar();getchar();
break;
default:
printf("\n无效输入,请重新选择...:");
while(exit!='y'&&exit!='Y');
答好的程序请发到1341066904@qq.com 急用谢了 满意的可再加分
#include<stdlib.h>
#define N 10 //顺序表的最大容量
int length=0; //顺序表的当前元素个数
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
#define LIST_INIT_SIZE 100//线性表存储的空间初始化分配量
#define LISTINCREAMENT 10 //线性表存储空间的分配增量
typedef struct LNode//线性单链表存储结构
int data;
struct LNode *next;
LNode,*LinkList;
int CreatList_L(LinkList&L)//创建一个线性链表
L=(LinkList)malloc(sizeof(LNode));//分配一个空间给链表,作为头结点
if(!L) exit(OVERFLOW);
L->next=NULL;
return OK;
int DestroyList_L(LinkList &L)//销毁链表
if(L) free(L);
return OK;
int ListInsert_L(LinkList&L,int i,int e)//再练表的第i个元素前插入一个元素e
LinkList p=L;//p指针定位于i-1
LNode *s;
int j=0;
while(p&&j<i-1) p=p->next;j++;//定位
if(!p||j>i-1) return ERROR;//如果i<1或大于链表元素个数+1
s=(LNode*)malloc(sizeof(LNode));
if(!s) exit(OVERFLOW);
s->data=e; //完成插入操作
s->next=p->next;
p->next=s;
return OK;
int ListDelet_L(LinkList&L,int i,int&e)//删除链表L中的第i个元素,并返回给e;
LinkList p=L;
LNode* q;
int j=0;
while(!p&&j<i-1) p=p->next;j++;//p指针定位于i-1;
if(!p->next||j>i-1) return ERROR;
e=p->next->data; //完成删除操作
q=p->next;
p->next=p->next->next;
free(q);
return OK;
int ListTraverse_L(LinkList L,int n)//链表的遍历
int i=0;
if(!L)return ERROR;
L=L->next;
while(L)
if(L->data==n)return i;
L=L->next;
i++;
return FALSE;
int InverseSingleList_L(LinkList &L)
if(!L->next||!L->next->next)//如果链表少于2个Node那么链表不需要改变顺序
return OK;
LNode *p,*q;
p=L->next; //第一次因为p是最后一个连接所以把p->next设为空
q=p->next;
p->next=NULL;
p=q;
while(p)
q=p->next; //用q去保留p后面一个Node;
p->next=L->next;
L->next=p;
p=q;
return OK;
int main()
int List[N];
LinkList L;
int ch,exit='N';
do
system("CLS");
printf("\t\t********************************************\n");
printf("\t\t* 1.创建一个顺序表 .........(1) *\n");
printf("\t\t* 2.在顺序表中查找元表.........(2) *\n");
printf("\t\t* 3.在顺序表中插入元表.........(3) *\n");
printf("\t\t* 4.在顺序表中删除元表.........(4) *\n");
printf("\t\t* 5.退出 .........(5) *\n");
printf("\t\t********************************************\n");
printf("\n请选择操作代码:");
ch=getchar();
switch(ch)
case '1':
printf("\n请输入十个元素");
CreatList_L(L);
for(length=0;length<N;length++)
scanf("%d",&List[length]);
getchar();
ListInsert_L(L,length+1,List[length]);
printf("\n创建成功!");
getchar();
break;
case '2':
scanf("%d",&List[0]);
if(ListTraverse_L(L,List[0]))printf("该元素存在该年表的第%d个位置",ListTraverse_L(L,List[0]));
else printf("不存在该元素");
getchar();
break;
case '3':
scanf("%d%d",&length,&List[0]);
ListInsert_L(L,length,List[0]);
system("pause");
break;
case '4':
scanf("%d",&length);
ListDelet_L(L,length,List[0]);
system("pause");
break;
case '5':
printf("\n您是否真的要退出程序(Y/N):");
getchar();
exit=getchar();
break;
default:
getchar();
printf("\n无效输入,请重新选择...:");
getchar();
break;
while(exit!='y'&&exit!='Y');
参考技术A 我是杨剑,你是哪个班的学生啊
c语言实现顺序表(初阶数据结构)
文章目录
前言
这是小编第一篇数据结构的文章,从今天开始更新数据结构相关的博客,希望大家多多关注,一起学习。
今天小编主要说的是顺序表的相关内容。
顺序表的介绍
说到顺序表,就需要介绍一下线性表了,线性表是什么呢?
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
现在知道了顺序表是线性表的一种,线性表还包括链表,栈、队列和字符串。
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
这里的数组也就是顺序表的基本结构,链式结构也就是链表的基本结构。这里我们先谈顺序表,以后再谈链表。
还需要补充一下逻辑结构和物理结构,这个没理解好,后面是不太好学的
逻辑结构:简单地理解,就是指的数据之间的逻辑关系。
物理结构:数据在物理存储空间上选择集中存放还是分散存放
单看这个可能不太好理解,举个简单的例子
人物的关系就是逻辑结构,我是爸爸的儿子,爸爸是爷爷的儿子,正是这种关系才把我们串联在一起,我们可以不在一起,但是我们在逻辑上相当于有一条线将我们串在一起。
排队时就是物理结构,我们必须站在一列,是什么把我们串联在一起,是排队这种物理结构,相当于有一条实质性的线,而且这条一旦断了,物理结构也就没了。
顺序表在物理和逻辑上都是连续的,而链表在逻辑是连续的,物理上是不连续的
接下来正式谈顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
何为顺序表?简单说就是数组
顺序表的分类
静态顺序表:使用定长数组存储。
动态顺序表:使用动态开辟的数组存储。
这里我们主要讲解动态版本,静态在c语言中也学过,就是一个普通数组,实际意义不大。
动态开辟是什么意思?也就是数组的容量不是确定的,是可以动态增长的,这样其实更有利于存储数据,支持增删等操作的。
接下来我们就开始实现
项目分配
小编是使用的是vs2019作为开发工具,搭建顺序表工程
- SeqList.h作为头文件,顺序表的创建、储存函数声明和引用函数所需的头文件
- SeqList.c作为函数接口文件,即需求函数的定义
- test 7_29_.c作为测试文件,看函数实现是否正确
顺序表的定义
typedef int SQDataType;
//动态顺序表的定义
typedef struct SeqList
{
SQDataType* a;//数组的名称
int size; //有效数据的个数
int capacity;//空间总容量大小
}SLT;
看这段代码,其实还是有一些小技巧,解决了一些重要的问题
在一个项目中我们需要在很多地方用到数据的类型名,如int、char。如果我们想把原本储存数据类型为int改为char,就需要改很多的地方,那么有没有什么一劳永逸的方法呢?
答案是有的,我们可以用到typedef,直接将int定义为SQDataType,后面全用SQDataType,以后要改的话,就只需将int改为char或者其他类型,结构体也是如此,定义为了SLT。
这个小问题就解决了。
定义完成后将这段程序放在SeqList.h中。
顺序表的初始化
//初始化
void SeqListInit(SLT* psl)
{
assert(psl);//断言psl一定不能为空,要引头文件assert.h
psl->a = NULL;
psl->size = psl->capacity = 0;
}
初始化需要将数组置为空,有效数据个数和大小均置为0,如果不进行初始化,将全部为随机值,可能会影响后续操作。
这里非常重要一点是传址,不是传值,记住一般需要修改数值时都是传址。
其实直接将main函数中创建的顺序表s直接s = {0};
来初始化也是可以的,只是上面这样写是为了让大家了解具体是如何实现的。
我们可以通过调试来查看是否成功。
成功!!!
顺序表的打印
已经建立了一个顺序表,如何才能清楚的看到呢?这就需要我们将他打印在屏幕上了
//打印函数
void SeqListPrint(SLT* psl)
{
assert(psl);
for (int i = 0; i < psl->size; i++)
{
printf("%d ", psl->a[i]);
}
printf("\\n");
}
打印就是简单的循环来完成就可以,需要注意的是传址。
等下讲尾插的时候可以检测是否正确。
顺序表的销毁
顺序表在使用完毕后,由于是动态开辟的内存空间,是需要free掉的,不然会造成内存泄漏,所以我们需要销毁这块已经使用过的空间。
//销毁函数
void SeqListDestory(SLT* psl)
{
assert(psl);
if (psl->a)//判断动态开辟的数组是否为空
{
free(psl->a);//不为空就释放掉
}
psl->size = psl->capacity = 0;
}
接下来都是些重要的函数接口了
顺序表的尾插
尾插其实就是从尾部插入数据,组成一个新的顺序表。
增容函数的实现
但是需要注意的是我们需要判断原有顺序表是否为空或者已满,如果为空或者已满我们就需要开辟新空间来储存插入的数据,所以需要写一个判断代码,再开辟新空间,由于还有其他的插入接口,所以我们可以直接内部封装一个增容函数SeqListCheclCapacity。
//封装一个增容函数
void SeqListCheclCapacity(SLT* psl)
{
if (psl->size == psl->capacity)//判断是否满了
{
//如果为空,大小就置为4,否则就以2倍递增
size_t newcapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
//动态开辟空间
psl->a = realloc(psl->a, newcapacity * sizeof(SQDataType));
psl->capacity = newcapacity;
}
}
这个增容代码需要注意的是调用realloc函数时,第二个参数是新的存储大小,单位为字节,所以传的应该是sizeof(存储的数据类型) * newcapacity(新大小),不能直接传新大小。
再就是解释为什么以二倍递增?二倍其实是一种折中的考虑,避免造成空间的浪费,要是以3倍、4倍也行。
尾插
//尾插
void SeqListPushBack(SLT* psl, SQDataType x)
{
assert(psl);
SeqListCheclCapacity(psl);
psl->a[psl->size] = x;
psl->size++;
}
先判断是否满了,满了就增容,然后就直接插入到size的位置,然后size++,有效个数加一。尾插就是这么简单。
我们来通过测试文件来看一下效果
完美创建,增容成功,打印也没有问题!!!
顺序表的头插
讲完尾插后,就是头插了,头插也就是从头部插入。
头部插入要比尾部插入麻烦一丢丢啦, 只需要将数据整体向后移动一位就可以,再在下标为0的位置插入就可以了,因为是插入,所以也需要考虑增容问题。
//头插
void SeqListPushFront(SLT* psl, SQDataType x)
{
assert(psl);
SeqListCheclCapacity(psl);//判断是否已满
int end = psl->size - 1;//找到尾部
while (end >= 0)
{
psl->a[end + 1] = psl->a[end];//整体向后移动一位
end--;
}
psl->a[0] = x;
psl->size++;
}
看一下效果
成功!!!
顺序表的任意位置插入数据
知道尾插和头插后一定就更想知道如何在任意位置插入了,其实任意位置插入也就和头插类似,也就是在将指定位置的后面所有数值均向后移动一位,而头插是固定的移动所有数值,这个移动要看要求,也就是传过来的pos值,pos值也就是下标值。
函数声明为
//在指定位置插入数值
void SeqListInsert(SLT* psl, size_t pos, SQDataType x);//pos为指定的下标位置,x为传过来的数值
//指定位置插入数值(可以代替头插和尾插)
void SeqListInsert(SLT* psl, size_t pos, SQDataType x)
{
assert(psl);
assert(pos <= psl->size);//插入的位置必须小于等于含有的数据数
SeqListCheclCapacity(psl);//判断是否需要增容
size_t end = psl->size;
//指定位置后的数据全部向后移动一位
while (end > pos)
{
psl->a[end] = psl->a[end - 1];
end--;
}
psl->a[pos] = x;
psl->size++;
}
这个代码是可以代替头插和尾插的
看一下效果
成功!!!
顺序表的尾删
讲完插入就要讲删除了,一些不要的数据是需要删除的,踢出我们的顺序表。删除也和插入一样,我们先来看尾删,其实尾删是非常简单的,我们只需要把有效数字的个数减一就行了,这样就访问不到这个数据了。
这里就有同学会问了,你这样不是数据还在吗?没有删除掉啊。
这里我想说的是,我们完全没有必要去把这个数据free掉,回想一下我们插入是怎么插入的,我们除了尾插其他都是通过后移数据来操作的,其实这个有点像的就是覆盖,所以如果要重新插入数据,也就是覆盖,所以这个删除数据在不在也就不影响,只是覆盖换值,我们所要做的也就是让顺序表访问不到就行了。
//尾删
void SeqListPopBack(SLT* psl)
{
assert(psl);
//assert(psl->size > 0); 不太温和的方式
//温和一点
if (psl->size > 0)
{
psl->size--;//有效数据个数减一
}
}
这里温和和不太温和的方式意思其实是一样的,就是防止顺序表为空时还要进行删除。不温合就直接报错,温和就是判断一下。
看一下效果
成功!!!
顺序表的头删
讲完尾删就是头删了,头删的主要操作也就是覆盖,除了下标为0的所有数据全部向前移动一位,将第一个数据覆盖掉,有效数据个数减一即可。
将2、3、4、5依次向前覆盖,再将size减一,所得到的的就是头删后的顺序表。
//头删
void SeqListPopFront(SLT* psl)
{
assert(psl);
int head = 1;
while (head < psl->size)//从下标为1的位置开始前移
{
psl->a[head - 1] = psl->a[head];
head++;
}
//assert(psl->size > 0); //不太温和的方式
//温和一点
if (psl->size > 0)
{
psl->size--;
}
}
看一下效果
成功!!!
顺序表的任意位置删除
讲完尾删和头删,接下来就是任意位置的删除,这个的主要思想也是覆盖,插入是往后覆盖,删除则是往前覆盖,在给定位置后的所有数据依次向前覆盖,再size减一。
//指定位置删除数据(可以代替头删和尾删)
void SeqListErase(SLT* psl, size_t pos)
{
assert(psl);
assert(pos < psl->size);
size_t begin = pos + 1;
while (begin < psl->size)
{
psl->a[begin - 1] = psl->a[begin];
begin++;
}
psl->size--;
}
看一下效果
成功!!!
顺序表的查找
查找就是看顺序表是否存在指定的数值,直接遍历判断即可,存在就返回下标,不存在就返回-1,所以函数的返回值为int类型。
//查找
int SeqListFind(SLT* psl, SQDataType x)
{
assert(psl);
for (int i = 0; i < psl->size; i++)
{
if (psl->a[i] == x)
{
return i;
}
}
return -1;
}
这个代码有个缺陷就是如果存在两个相同的数值,就只会返回下标小的那个,不会全部返回。这里只需要要求我们判断存不存在就够了。
看一下效果
成功!!!
顺序表的元素个数
直接将size打印即可,由于个数一定是大于等于0的,所以函数的返回值类型可以为size_t。
/求出顺序表中的元素数目
size_t SeqListSize(SLT* psl)
{
assert(psl);
return psl->size;
}
看一下效果
成功!!!
修改指定位置的数值
直接赋值替换就行。
//修改指定位置的值
void SeqListAt(SLT* psl, size_t pos, SQDataType x)
{
assert(psl);
assert(pos < psl->size);//位置要小于现有的数值个数
psl->a[pos] = x;
}
看一下效果
成功!!!
结语
这就是顺序表所要掌握的所有函数接口,相信你肯定对顺序表有了一定了解,整体来看,顺序表的缺点还是很明显的,就是空间的浪费,实际使用的空间是常常小于所开辟的空间的,并且增容拷贝数据也是消耗很大的,所以链表就出现了。小编将在不久出一篇关于链表的博客。
不过当下为了巩固大家顺序表的理解,小编大概在两天后更新一篇关于顺序表的几题力扣oj题,欢迎大家关注,共同学习,如果在阅读时发现错误,可以私信我修改,如果不理解,也可以来讨论,私信必回哦。感谢大家参考学习。
SeqList.h文件内容
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SQDataType;
//动态顺序表的定义
typedef struct SeqList
{
SQDataType* a;
int size; //有效数据的个数
int capacity;//空间总容量大小
}SLT;
//增删查改
//初始化
void SeqListInit(SLT* psl);
//销毁函数
void SeqListDestory(SLT* psl);
//打印函数
void SeqListPrint(SLT* psl);
//封装一个增容函数
void SeqListCheclCapacity(SLT* psl);
//尾插
void SeqListPushBack(SLT* psl, SQDataType x);
//头插
void SeqListPushFront(SLT* psl, SQDataType x);
//尾删
void SeqListPopBack(SLT* psl);
//头删
void SeqListPopFront(SLT* psl);
//查找
int SeqListFind(SLT* psl, SQDataType x);
//在指定位置插入数值
void SeqListInsert(SLT* psl, size_t pos, SQDataType x);
//删除指定位置的数值
void SeqListErase(SLT* psl, size_t pos);
//求出顺序表中的元素数目
size_t SeqListSize(SLT* psl);
//修改指定位置的值
void SeqListAt(SLT* psl, size_t pos, SQDataType x);
test 7_29_.c文件内容
#pragma once
#include"SeqList.h"
//初始化
void SeqListInit(SLT* psl)
{
assert(psl);//psl一定不能为空
psl->a = NULL;
psl->size = psl->capacity = 0;
}
//销毁函数
void SeqListDestory(SLT* psl)
{
assert(psl);
if (psl->a)
{
free(psl->a);
}
psl->size = psl->capacity = 0;
}
//打印函数
void SeqListPrint(SLT* psl)
{
assert(psl);
for (int i = 0; i < psl->size; i++)
{
printf("%d ", psl->a[i]);
}
printf("\\n");
}
//封装一个增容函数
void SeqListCheclCapacity(SLT* psl)
{
if (psl->size == psl->capacity)
{
size_t newcapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
psl->a = realloc(psl->a, newcapacity * sizeof(SQDataType));
psl->capacity = newcapacity;
}
}
//尾插
void SeqListPushBack(SLT* psl, SQDataType x)
{
assert(psl);
SeqListCheclCapacity(psl);
psl->a[psl->size] = x;
psl->size++;
}
//头插
void SeqListPushFront(SLT* psl, SQDataType x)
{
assert(psl);
SeqListCheclCapacity(psl);
int end = psl->size - 1;
while (end >= 0)
{
psl->a[end + 1] = psl->a[end];
end--;
}
psl->a[0] = x;
psl->size++;
}
//尾删
void SeqListPopBack(SLT* psl)
{
assert(psl);
//assert(psl->size > 0); 不太温和的方式
//温和一点
if (psl->size > 0)
{
psl->size--;
}
}
//头删
void SeqListPopFront(SLT* psl)
{
assert(psl);
int head = 1;
while (head < psl->size)
{
psl->a[head - 1] = psl->a[head];
head++;
}
//assert(psl->size > 0); 不太温和的方式
//温和一点
if (psl->size > 0)
{
psl->size--;
}
}
//查找
int SeqListFind(SLT* psl, SQDataType x)
{
assert(psl);
for (int i = 0; i < psl->size; i++)
{
if (psl->a[i] == x)
{
return i;
}
}
return -1;
}
//指定位置插入数值(可以代替头插和尾插)
void SeqListInsert(SLT* psl, size_t pos, SQDataType x)
{
assert(psl);
assert(pos <= psl->size);//插入的位置必须小于等于含有的数据数
SeqListCheclCapacity(psl);
size_t end = psl->size;
while (end > pos)
{
psl->a[end] = psl->a[end - 1];
end--;
}
psl->a[pos] = x;
psl->sizeC语言数据结构顺序表的查找/插入/删除操作
数据结构实验:线性表的顺序表示和链式表示及插入、删除、查找运算