【数据结构】顺序表的概念和实现【完整版】

目录​​​​​​​

一、简述:

二、顺序表的概念

三、顺序表的创建


一、简述:​​​​​​​

顺序表的创建和功能实现(插入,删除,排序等等)和通讯录的实现十分相似,换言之,你会通讯录了,顺序表就是小菜一碟,所以不知道通讯录的实现的话,可以先看一下我写的通讯录的实现过程,这个顺序表只是在通讯录的逻辑上稍加修改就实现了,可以说顺序表是通讯录的副本

我们分3个文件:

seqlist.h文件放——头文件的包含和函数声明

seqlist.c文件放——函数的定义和实现

test.c文件——用来测试写的顺序表的相关逻辑

而且这个程序不建议先写菜单,因为写菜单后你调试麻烦,你每次都需要选择菜单的选项,才能往下调试,所以菜单的实现可以放在最后,并且调试时断点可以移动,但是已经调试的代码不能恢复到没执行的状态,什么时候用?比如我们动态开辟内存成功了,我就想看看开辟没成功的时候,我们就可以拖动断点,看看开辟失败程序是怎么走的

二、顺序表的概念

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。换句话说,顺序表就是数组,但是在数组的基础上,他还要求数据是连续存储的,不能跳跃间隔,即比如a是个数组,存a[5] =0,然后再存a[9] =0这对数组来说是可以的,但对于顺序表,你只能在存完a[5] =0后,存a[6] = 0。

三、顺序表的创建

分为静态顺序表和动态顺序表,但是静态顺序表大小有些固定,所以我们用动态开辟内存函数来创建动态顺序表

//静态版本

//N给小了不够用,给大了会浪费

#define N 10000
typedef int SLDataType;

//静态顺序表
typedef struct SeqList
{
    SLDataType a[N];
    int sz;//数组中存储了多少个数据
}SL;
//动态版本

//和通讯录中通讯录的结构体创建十分相似

#include<stdlib.h>

typedef int SLDataType;

//动态顺序表
typedef struct SeqList
{
    SLDataType* a;
    int sz;//当前顺序表存的有效数据的个数
    int capacity;//当前顺序表的最大容量的个数
}SL;

接下来是函数的相关实现细节,这里说的函数也可以叫做接口函数,接口函数就是某个模块写了主要给其他模块用的函数

1、初始化

//顺序表初始化的实现
void SeqListInit(SL* pc)
{
	pc->a = NULL;
	pc->sz = pc->capacity = 0;//连续赋值
}

2、尾插数据和检查容量

增加数据就要考虑顺序表容量是否够。从顺序表结尾插入数据也属于添加数据,所以要考虑这个顺序表的容量问题,因为我是动态开辟的,先写一个检查容量函数CheckCapacity

有必要说的一点是:越界并不会报错,而是在free释放内存的时候会报错

void CheckCapacity(SL* pc)
{
	if (pc->sz == pc->capacity)
	{
		int newcapacity = pc->capacity == 0 ? 4 : pc->capacity * 2;
		//因为==的优先级高于?: 所以先判断是否容量为0
		//这个操作的成功性在于了解运算符的优先级性
		SLDataType* ptr = (SLDataType*)realloc(pc->a, newcapacity * sizeof(SLDataType));
		if (ptr != NULL)
		{
			pc->a = ptr;
			pc->capacity = newcapacity;
		}
		else
		{
			printf("realloc fail\n");
			exit(-1);
			//开辟都失败了,这个程序就没必要往下走了
			//直接一个exit,终止程序
		}
	}
}
//顺序表尾插数据的实现
void SeqListPushBack(SL* pc, SLDataType x)
{
	//先检查容量,不够就进行扩容
	CheckCapacity(pc);
	pc->a[pc->sz] = x;
	pc->sz++;
}

3、打印顺序表数据的函数

这个操作很简单,我们只需要遍历到当前顺序表有效数据个数(sz),打印即可

//顺序表打印数据的函数的实现
void SeqListPrint(SL* pc)
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%d ", pc->a[i]);
	}
	printf("\n");
}

​​​​​​​4、尾删数据

//顺序表尾删数据的实现
void SeqListPopBack(SL* pc)
{
//删除的前提是顺序表有元素
//assert条件为真,往下执行,为假,直接程序报错
//这是一个比较粗暴的方式,也可以用if条件判断
//assert记得引头文件
	assert(pc->sz > 0);
	pc->sz--;
}

5、头插数据

//顺序表头插数据的函数实现
void SeqListPushFront(SL* pc,SLDataType x)
{
	CheckCapacity(pc);
	//挪动数据
	int end = pc->sz - 1;
	while (end >= 0)
	{
		pc->a[end + 1] = pc->a[end];
		end--;
	}
	pc->a[0] = x;
	pc->sz++;
}

 6、头删数据

//顺序表头删数据的函数实现
void SeqListPopFront(SL* pc)
{
	assert(pc->sz > 0);
	//挪动数据
	int begin = 0;
	while (begin < pc->sz-1)
	{
		pc->a[begin] = pc->a[begin + 1];
		begin++;
	}
	pc->sz--;
}

7、查找顺序表元素

如果找到了这个元素返回这个元素对应的下标,没找到则返回-1

//查找顺序表中数据的函数实现
//规定找到范围数组元素下标
//没找到返回-1
int SeqListFind(SL* pc, SLDataType x)
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (pc->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}

8、顺序表的指定位置插入

这个函数你需要传你想在哪个位置,插入什么元素,这个位置指的是数组的元素下标,指定位置插入,那么你指定的这个元素下标肯定不能越界,所以先要判断一下并且还要考虑容量问题

//顺序表中插入指定位置(数组下标为pos)的数据的函数
void SeqListInsert(SL* pc,int pos,SLDataType x)
{	//温柔的处理方式
	//插入位置的检查,不能越界
	//if (pos > pc->sz || pos < 0)
	//{
	//	//这里为什么不用perror直接打印动态开辟的错误信息?
	//	//因为perror是调用c语言的一些库函数失败的时候去打印的
	//	//他会打印库函数调用失败的原因,比如main,fopen函数等
	//	printf("pos invalid\n");
	//	//这里不用perror是因为pos可能是因为传参失败
	//	//不是库函数调用失败问题,所以不用
	//	return;
	//}
	//粗暴的处理方式
	assert(pos <= pc->sz && pos >= 0);
	CheckCapacity(pc);
	int end = pc->sz;
	while (end > pos)
	{
		pc->a[end] = pc->a[end-1];
		end--;
	}
	pc->a[pos] = x;
	pc->sz++;
}

9​​​​​​​、顺序表中删除指定位置的数据

这个函数你需要传你想在哪个位置,删除什么元素,这个位置指的是数组的元素下标,指定位置删除,那么你指定的这个元素下标肯定不能越界,所以先要判断一下这个元素是否存在,我给定的范围是否越界

//顺序表中删除指定位置的数据的函数
void SeqListErase(SL* pc, int pos)
{
	assert(pos >= 0 && pos < pc->sz);
	int begin = pos;
	while (begin < pc->sz-1)
	{
		pc->a[begin] = pc->a[begin + 1];
		begin++;
	}
	pc->sz--;
}

​​​​​​​10、销毁顺序表

销毁其实就是因为动态开辟的内存,需要手动返还给操作系统,不然会导致内存泄漏的问题

//销毁顺序表的函数实现
void SeqListDestroy(SL* pc)
{
	free(pc->a);
	pc->a = NULL;
	pc->capacity = 0;
	pc->sz = 0;
}

 最后,源码如下,相关实现逻辑均在代码的注释中!

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"seqlist.h"
void menu()
{
	printf("************************\n");
	printf("*** 1、头插   2、头删 ***\n");
	printf("*** 3、尾插   4、尾删 ***\n");
	printf("*** 5、打印   6、查找 ***\n");
	printf("*** 7、 指定位置插入  ***\n");
	printf("*** 8、 指定位置删除  ***\n");
	printf("***     0、退出      ***\n");
	printf("************************\n");
}
int main()
{
	int input = 0;
	SL ps = { 0 };
	int pos = 0;
	SLDataType x = 0;
	//顺序表的初始化
	SeqListInit(&ps);
	do
	{
		menu();
		printf("请输入你想进行的操作:>\n");
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			printf("退出\n");
			//销毁顺序表
			SeqListDestroy(&ps);
			break;
		case 1:
			//头插数据
			printf("请输入你要头插的数据\n");
			while(scanf("%d", &x) != EOF)
			SeqListPushFront(&ps, x);
			break;
		case 2:
			//头删数据
			SeqListPopFront(&ps);
			break;
		case 3:
			//尾插数据
			printf("请输入你要尾插的数据\n");
			while (scanf("%d", &x) != EOF)
			SeqListPushBack(&ps, x);
			break;
		case 4:
			//尾删数据
			SeqListPopBack(&ps);
			break;
		case 5:
			//打印顺序表
			SeqListPrint(&ps);
			break;
		case 6:
			//查找顺序表中的某个元素,pos是数组下标
			printf("请输入你要查找的数据\n");
			scanf("%d", &x);
			pos = SeqListFind(&ps,x);
			if (pos == -1)
				printf("你要查找的元素不存在\n");
			else
				printf("该元素下标为%d\n",pos);
			break;
		case 7:
			//指定位置插入数据
			printf("请输入你要插入位置的下标和插入的数据\n");
			while (scanf("%d %d",&pos, &x) != EOF)
			SeqListInsert(&ps, pos, x);
			break;
		case 8:
			//指定位置删除数据
			printf("请输入你要删除位置的下标\n");
			while (scanf("%d", &pos) != EOF)
			SeqListErase(&ps, pos);
			break;
		default:
			printf("无此选项,请重新输入!\n");
		}
	} while (input);
	return 0;
}

SeqList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"seqlist.h"
//顺序表初始化的实现
void SeqListInit(SL* pc)
{
	pc->a = NULL;
	pc->sz = pc->capacity = 0;//连续赋值
}

//顺序表检查容量的函数
void CheckCapacity(SL* pc)
{
	if (pc->sz == pc->capacity)
	{
		int newcapacity = pc->capacity == 0 ? 4 : pc->capacity * 2;
		//因为==的优先级高于?: 所以先判断是否容量为0
		//这个操作的成功性在于了解运算符的优先级性
		SLDataType* ptr = (SLDataType*)realloc(pc->a, newcapacity * sizeof(SLDataType));
		if (ptr != NULL)
		{
			pc->a = ptr;
			pc->capacity = newcapacity;
		}
		else
		{
			printf("realloc fail\n");
			exit(-1);
			//开辟都失败了,这个程序就没必要往下走了
			//直接一个exit,终止程序
			//exit正常退出返回0  exit(0)
			//exit异常退出,写个-1就行 exit(-1)
		}
	}
}
//顺序表尾插数据的实现
void SeqListPushBack(SL* pc, SLDataType x)
{
	//先检查容量,不够就进行扩容
	CheckCapacity(pc);
	pc->a[pc->sz] = x;
	pc->sz++;
}

//顺序表尾删数据的实现
void SeqListPopBack(SL* pc)
{
//删除的前提是顺序表有元素
//assert条件为真,往下执行,为假,直接程序报错
//这是一个比较粗暴的方式,也可以用if条件判断
//assert记得引头文件
//可以直接pc->sz--是因为顺序表是连续存储不间断的
//--后一定是把最后一个元素删掉的,而头删肯定就不能这么做
	assert(pc->sz > 0);
	pc->sz--;
}


//顺序表打印数据的函数的实现
void SeqListPrint(SL* pc)
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%d ", pc->a[i]);
	}
	printf("\n");
}

//销毁顺序表的函数实现
void SeqListDestroy(SL* pc)
{
	free(pc->a);
	pc->a = NULL;
	pc->capacity = 0;
	pc->sz = 0;
}

//顺序表头插数据的函数实现
void SeqListPushFront(SL* pc,SLDataType x)
{
	CheckCapacity(pc);
	//挪动数据
	int end = pc->sz - 1;
	while (end >= 0)
	{
		pc->a[end + 1] = pc->a[end];
		end--;
	}
	pc->a[0] = x;
	pc->sz++;
}

//顺序表头删数据的函数实现
void SeqListPopFront(SL* pc)
{
	assert(pc->sz > 0);
	//挪动数据
	int begin = 0;
	while (begin < pc->sz-1)
	{
		pc->a[begin] = pc->a[begin + 1];
		begin++;
	}
	pc->sz--;
}

//查找顺序表中数据的函数实现
//规定找到范围数组元素下标
//没找到返回-1
int SeqListFind(SL* pc, SLDataType x)
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (pc->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}

//顺序表中插入指定位置(数组下标为pos)的数据的函数
void SeqListInsert(SL* pc,int pos,SLDataType x)
{	//温柔的处理方式
	//插入位置的检查,不能越界
	//if (pos > pc->sz || pos < 0)
	//{
	//	//这里为什么不用perror直接打印动态开辟的错误信息?
	//	//因为perror是调用c语言的一些库函数失败的时候去打印的
	//	//他会打印库函数调用失败的原因,比如main,fopen函数等
	//	printf("pos invalid\n");
	//	//这里不用perror是因为pos可能是因为传参失败
	//	//不是库函数调用失败问题,所以不用
	//	return;
	//}
	//粗暴的处理方式
	assert(pos <= pc->sz && pos >= 0);
	CheckCapacity(pc);
	int end = pc->sz;
	while (end > pos)
	{
		pc->a[end] = pc->a[end-1];
		end--;
	}
	pc->a[pos] = x;
	pc->sz++;
}

//顺序表中删除指定位置的数据的函数
void SeqListErase(SL* pc, int pos)
{
	assert(pos >= 0 && pos < pc->sz);
	int begin = pos;
	while (begin < pc->sz-1)
	{
		pc->a[begin] = pc->a[begin + 1];
		begin++;
	}
	pc->sz--;
}

SeqList.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLDataType;

//动态顺序表
typedef struct SeqList
{
	SLDataType* a;
	int sz;//当前顺序表存的有效数据的个数
	int capacity;//当前顺序表的最大容量的个数
}SL;

//顺序表初始化的声明
void SeqListInit(SL* pc);
//顺序表尾插函数的声明
void SeqListPushBack(SL* pc, SLDataType x);
//顺序表尾删函数的声明
void SeqListPopBack(SL* pc);
//顺序表打印数据的函数声明
void SeqListPrint(SL* pc);
//顺序表销毁函数的声明(因为是动态开辟的,内存需要释放)
void SeqListDestroy(SL* pc);
//顺序表头插数据的函数声明
void SeqListPushFront(SL* pc,SLDataType x);
//顺序表头删数据的函数声明
void SeqListPopFront(SL* pc);
//找顺序表中数据的函数声明
int SeqListFind(SL* pc, SLDataType x);
//顺序表中插入指定位置的数据的函数声明
void SeqListInsert(SL* pc, int pos, SLDataType x);
//顺序表中删除指定位置的数据的函数声明
void SeqListErase(SL* pc, int pos);

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
顺序表是一种简单的数据结构,它可以用一块连续的内存空间存储具有相同类型的数据元素。下面是顺序表实现: ```c++ #define MaxSize 100 // 定义最大长度 typedef struct { int data[MaxSize]; // 用数组存储数据元素 int length; // 当前长度 } SqList; // 初始化顺序表 void InitList(SqList &L) { for (int i = 0; i < MaxSize; i++) { L.data[i] = 0; // 将数组元素全部赋值为0 } L.length = 0; // 初始化顺序表长度为0 } // 插入元素 bool InsertList(SqList &L, int i, int x) { 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] = x; // 在位置i处插入新元素x L.length++; // 长度加1 return true; } // 删除元素 bool DeleteList(SqList &L, int i) { if (i < 1 || i > L.length) { // 判断i的范围是否有效 return false; } for (int j = i; j < L.length; j++) { // 将第i个元素之后的元素前移 L.data[j - 1] = L.data[j]; } L.length--; // 长度减1 return true; } // 查找元素 int SearchList(SqList L, int x) { for (int i = 0; i < L.length; i++) { if (L.data[i] == x) { return i + 1; // 返回元素的位置 } } return 0; // 未找到返回0 } // 输出顺序表 void PrintList(SqList L) { for (int i = 0; i < L.length; i++) { cout << L.data[i] << " "; } cout << endl; } ``` 其中,`InitList`函数用于初始化顺序表,`InsertList`函数用于在顺序表的第`i`个位置插入元素`x`,`DeleteList`函数用于删除顺序表的第`i`个元素,`SearchList`函数用于查找元素`x`在顺序表中的位置,`PrintList`函数用于输出顺序表中的所有元素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值