目录
前言
顺序表是线性表的一种,是以连续的地址内存存储单元依次存储线性表的数据元素,这种结构就被称为线性表。
现在我们就实现一下顺序表的各种功能。
一、对顺序表基本创建销毁功能的实现
首先我们对顺序表要实现的基础接口功能先实现出来,有创建、初始化、销毁三个。
1.顺序表结构体的定义和创建
typedef int SLDataType;//将类型重定义为SLDataType
typedef struct SeqList {
SLDataType* data;
int size;
int capacity;
}SeqList;
data用来存储数据,size为有效数据的空间数量,capacity是可存储的空间数量。
这里我们使用SLDataType指针类型,是为了使用动态内存分配空间,以达到空间的灵活。
2.初始化顺序表
void SeqListInit(SeqList* pc) {
pc->data = NULL;
pc->size = 0;
pc->capacity = 0;
}
3.顺序表的销毁
void SeqListDestroy(SeqList* pc) {
assert(pc);
if (pc->data != NULL) {
free(pc->data);
pc->data = NULL;
pc->size = 0;
pc->capacity = 0;
}
}
销毁时我们要注意的是,如果pc.data里本身就为NULL空时,对NULL空进行free是会出现错误的,所以我们在销毁free掉的时候要进行判断,不能对NULL进行销毁,避免发生错误。
二、顺序表增删查改功能实现
我们对顺序表的基础创建功能实现好后,就要添加功能了,如增加数据、删除数据、查找数据、修改数据等等,这里我们就实现几个基础功能供读者去实现和理解,拓展功能读者有想法的可以自己去尝试的实现一下,比如排序数据、数据求和等功能。
1.封装代码-检测容量空间
我们在进行插入数据时,必须要先检测顺序表是否有空间进行插入数据,没分配空间或空间不足进行插入数据就会出现报错问题。(稍后给大家演示报错情况)
void SLCheckCapacity(SeqList* pc) {
assert(pc);
if (pc->capacity == pc->size) {
int newCapacity = pc->capacity == 0 ? 4 : pc->capacity * 2;
SLDataType* emp = (SLDataType*)realloc(pc->data, sizeof(SeqList) * newCapacity);
if (emp == NULL) {
perror("realloc emp");
exit(-1);
}
pc->data = emp;
pc->capacity = newCapacity;
}
}
2.尾插添加数据
尾插相对来说就比较简单了,只要检测空间足够,在size的有效空间里插入数据即可。
void SeqListPushBack(SeqList* pc, int x) {
assert(pc);
SLCheckCapacity(pc);
pc->data[pc->size] = x;
pc->size++;
}
这边给大家测试一下,如果不对空间进行判断,当无空间或者空间不够的时候,访问数据并插入会出现什么现象:
诶,报错啦,所以在添加数据的时候要记得检测是否有足够的空间喔。
3.打印数据
折腾了这么久,我们输入了数据当然也想要看看成果的呀,那我们创建一个打印数据的接口吧。
void SeqListPrint(SeqList* pc) {
assert(pc);
//assert(pc->size > 0);//暴力
if (pc->size == 0) {
printf("无数据打印失败\n");
return;
}
for (int i = 0; i < pc->size; i++) {
printf("%d ", pc->data[i]);
}
printf("\n");
}
测试:
我们打印数据肯定也是要看看有没有数据的,在pc.size里的有效数据如果为0的话,我们去打印就没有必要了,我们可以用两种方法,一种是暴力的办法,一种是温柔的,像这种我还是偏爱温柔一些的hhh,没有数据那就告诉我就好啦,没必要强行终止我的T_T。
方法一:
方法二:
哈哈哈,这样看了看,我还是喜欢温柔的多一些。
4.尾删数据
尾删其实跟尾插数据差不多一样简单的,差异就是要检测一下pc.size是否有数据,没有数据还删他就过分了,具体代码实现:
void SeqListPopBack(SeqList* pc) {
assert(pc);
//assert(pc->size != 0);//暴力
if (pc->size == 0) {
printf("没数据删除失败\n");
return;
}
pc->size--;
}
5.头插数据
在顺序表头部插入数据,我们需要把全部数据往后移动一位,再把需要插入的数据放到第一位就好了,代码实现如下:
void SeqListPushFront(SeqList* pc, int x) {
assert(pc);
SLCheckCapacity(pc);
for (int i = pc->size; i > 0; i--) {
pc->data[i] = pc->data[i - 1];
}
pc->data[0] = x;
pc->size++;
}
6.头删数据
头删跟尾删差不多,就多了个算法,要把全部数据往前移动一位就好了。
void SeqListPopFront(SeqList* pc) {
assert(pc);
//assert(pc->size != 0);//暴力
if (pc->size == 0) {
printf("没数据删除失败\n");
return;
}
for (int i = 1; i < pc->size; i++) {
pc->data[i - 1] = pc->data[i];
}
pc->size--;
}
7.查询指定数据返回数据位置
当我们插入数据后,想查找该数据的位置在哪,我们可以专门写一个接口去实现,当找不到后我们可以告诉用户,并用-1表示查找失败,代码实现:
int SeqListFind(SeqList* pc, int x) {
assert(pc);
//assert(pc->size > 0);//暴力
if (pc->size == 0) {
printf("无数据查找失败\n");
return;
}
for (int i = 0; i < pc->size; i++) {
if (pc->data[i] == x)
return i + 1;
}
printf("数据不存在\n");
return -1;
}
8.指定位置插入数据
顺序表其中一个重要功能就是指定位置插入数据,我们在指定位置插入时,要判断用户位置的合法性,我们不可能在0或者pc.size以外的位置插入数据的,这是我们要进行的一个判断,那插入数据挪位,其实跟头插差不多,只是数据上有些调整,代码如下:
void SeqListInsert(SeqList* pc, int pos, SLDataType x) {
assert(pc);
SLCheckCapacity(pc);
if ((0 > pos || pos > pc->size)) {
printf("位置错误,添加失败\n");
return;
}
int s = pos - 1;
for (int i = pc->size; i > s; i--) {
pc->data[i] = pc->data[i - 1];
}
pc->data[s] = x;
pc->size++;
}
9.指定位置删除数据
有添加数据,自然也有删除数据,指定删除的话,也是跟插入差不多,删除功能该判断的也是要判断的,位置的合法性,删除跟头删差不多,相对应的移动数据,代码如下:
void SeqListErase(SeqList* pc, int pos) {
assert(pc);
//assert(pc->size != 0);//暴力
if (pc->size == 0 || (0 > pos || pos > pc->size)) {
printf("删除失败\n");
return;
}
int s = pos - 1;
for (int i = s + 1; i < pc->size; i++) {
pc->data[i - 1] = pc->data[i];
}
pc->size--;
}
三、代码测试
顺序表是简单的线性表,我们在此就不编写菜单了,如读者有兴趣也可以自己编写一个的,我们对各功能的大致测试一下,尾插和尾删测试:
头插、头删功能测试:
指定查找功能测试:
这边我使用了尾插和头插,并查找20的位置。
指定位置插入、删除数据:
这里展示了合法位置添加或删除数据就能成功实现,非合法位置就提示错误或失败。
四、源码
SeqList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDataType;
typedef struct SeqList {
SLDataType* data;
int size;
int capacity;
}SeqList;
void SeqListInit(SeqList *pc);//初始化线性表
void SeqListDestroy(SeqList* pc);//销毁线性表
void SeqListPushBack(SeqList* pc, int x);//尾插
void SeqListPopBack(SeqList* pc);//尾删
void SeqListPushFront(SeqList* pc, int x);//头插
void SeqListPopFront(SeqList* pc);//头删
int SeqListFind(SeqList* pc, int x);//查找
void SeqListInsert(SeqList* pc, int pos, SLDataType x);//指定位置插入
void SeqListErase(SeqList* pc, int pos);//指定位置删除
void SeqListPrint(SeqList* pc);//打印
SeqList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
void SeqListInit(SeqList* pc) {
pc->data = NULL;
pc->size = 0;
pc->capacity = 0;
}
void SeqListDestroy(SeqList* pc) {
assert(pc);
if (pc->data != NULL) {
free(pc->data);
pc->data = NULL;
pc->size = 0;
pc->capacity = 0;
}
}
void SLCheckCapacity(SeqList* pc) {
assert(pc);
if (pc->capacity == pc->size) {
int newCapacity = pc->capacity == 0 ? 4 : pc->capacity * 2;
SLDataType* emp = (SLDataType*)realloc(pc->data, sizeof(SeqList) * newCapacity);
if (emp == NULL) {
perror("realloc emp");
exit(-1);
}
pc->data = emp;
pc->capacity = newCapacity;
}
}
void SeqListPushBack(SeqList* pc, int x) {
assert(pc);
SLCheckCapacity(pc);
pc->data[pc->size] = x;
pc->size++;
}
void SeqListPopBack(SeqList* pc) {
assert(pc);
//assert(pc->size != 0);//暴力
if (pc->size == 0) {
printf("没数据删除失败\n");
return;
}
pc->size--;
}
void SeqListPushFront(SeqList* pc, int x) {
assert(pc);
SLCheckCapacity(pc);
for (int i = pc->size; i > 0; i--) {
pc->data[i] = pc->data[i - 1];
}
pc->data[0] = x;
pc->size++;
}
void SeqListPopFront(SeqList* pc) {
assert(pc);
//assert(pc->size != 0);//暴力
if (pc->size == 0) {
printf("没数据删除失败\n");
return;
}
for (int i = 1; i < pc->size; i++) {
pc->data[i - 1] = pc->data[i];
}
pc->size--;
}
int SeqListFind(SeqList* pc, int x) {
assert(pc);
//assert(pc->size > 0);//暴力
if (pc->size == 0) {
printf("无数据查找失败\n");
return;
}
for (int i = 0; i < pc->size; i++) {
if (pc->data[i] == x)
return i + 1;
}
printf("数据不存在\n");
return -1;
}
void SeqListInsert(SeqList* pc, int pos, SLDataType x) {
assert(pc);
SLCheckCapacity(pc);
if ((0 > pos || pos > pc->size)) {
printf("位置错误,添加失败\n");
return;
}
int s = pos - 1;
for (int i = pc->size; i > s; i--) {
pc->data[i] = pc->data[i - 1];
}
pc->data[s] = x;
pc->size++;
}
void SeqListErase(SeqList* pc, int pos) {
assert(pc);
//assert(pc->size != 0);//暴力
if (pc->size == 0 || (0 > pos || pos > pc->size)) {
printf("删除失败\n");
return;
}
int s = pos - 1;
for (int i = s + 1; i < pc->size; i++) {
pc->data[i - 1] = pc->data[i];
}
pc->size--;
}
void SeqListPrint(SeqList* pc) {
assert(pc);
//assert(pc->size > 0);//暴力
if (pc->size == 0 ) {
printf("无数据打印失败\n");
return;
}
for (int i = 0; i < pc->size; i++) {
printf("%d ", pc->data[i]);
}
printf("\n");
}
总结
以上就是顺序表实现的主要内容啦,本文仅仅简单实现了顺序表的基本功能,而顺序表是对刚学数据结构的同学铺垫了大量能使我们快速理解和了解算法函数以及提升代码编写逻辑能力的,有能力的同学我建议都去实现一遍喔。