目录
在实际应用中有多种不同的存储、组织数据的方式,也就是我们所了解的数据结构,数据结构有很多种,都有不同的适用场景,我们首先了解的就是线性表中的一种——顺序表
一、什么是顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据的元素的线性结构,一般情况下是基于数组完成的,在数组上完成增删查改。
二、顺序表的分类
顺序表一般可以分为两种:
1、静态顺序表
使用定长数组来存储元素,缺点也十分明显:不能够灵活的改变存储数据的空间的大小
//静态顺序表
#define N 10
typedef int SLdatetype;
struct SeqList
{
SLdatetype a[10];
int size;
};
静态顺序表的缺点十分明显,存储空间的大小固定,不能扩容,使用前要随着存储数据的大小来设定存储空间的大小。
2、动态顺序表
使用动态开辟的数组的存储
三、接口
//初始化
void InItSeqList(SeqList* PSL);
//销毁
void DestroySeqList(SeqList* PSL);
//尾部插入
void SLPushBack(SeqList* PSL, SLdatetype x);
//头插
void SLPushFront(SeqList* PSL, SLdatetype x);
//尾删
void SLPopBack(SeqList* PSL);
//头删
void SLPopFront(SeqList* PSL);
//打印
void SLprintf(SeqList* PSL);
//指定位置插入
void SLInsert(SeqList* PSL, int pos, SLdatetype x);
//指定位置删除
void SLerase(SeqList* PSL, int pos);
//查找
int SLFind(SeqList* PSL, SLdatetype x);
//指定位置修改
void SLModify(SeqList* PSL, int pos, SLdatetype x);
四、实现
1、初始化
void InItSeqList(SeqList* PSL)
{//初始化
assert(PSL);
PSL->pa = (SLdatetype*)malloc(sizeof(SLdatetype) * 4);
//初始化开辟4个SLdatetype类型大小的空间
if (PSL->pa == NULL)
{
perror("PSL->pa");
return;
}
PSL->size = 0;
PSL->capacity = 4; //容量为4
}
2、销毁
void DestroySeqList(SeqList* PSL)
{//销毁
assert(PSL);
free(PSL->pa); //要释放开辟的空间
PSL->pa = NULL;
PSL->size = 0;
PSL->capacity = 0;
}
3、尾部插入
void CheckCapacity(SeqList* PSL)
{//检查是否越界
assert(PSL);
if (PSL->capacity == PSL->size)
{//容量耗尽扩容
SLdatetype* tmp = (SLdatetype*)realloc(PSL->pa, sizeof(SLdatetype) * PSL->capacity * 2);
if (tmp == NULL)
{
perror("realloc");
return;
}
PSL->pa = tmp;
PSL->capacity *= 2;
}
}
void SLPushBack(SeqList* PSL, SLdatetype x)
{//尾部插入
assert(PSL);
CheckCapacity(PSL);
SLInsert(PSL, PSL->size + 1, x);
//调用插入接口,在最后一个元素的后面插入
}
4、头插
void CheckCapacity(SeqList* PSL)
{//检查是否越界
assert(PSL);
if (PSL->capacity == PSL->size)
{//容量耗尽扩容
SLdatetype* tmp = (SLdatetype*)realloc(PSL->pa, sizeof(SLdatetype) * PSL->capacity * 2);
if (tmp == NULL)
{
perror("realloc");
return;
}
PSL->pa = tmp;
PSL->capacity *= 2;
}
}
void SLPushFront(SeqList* PSL, SLdatetype x)
{//头插
assert(PSL);
CheckCapacity(PSL); //检查容量
SLInsert(PSL, 1, x); //复用SLInsert
}
5、尾删
void SLPopBack(SeqList* PSL)
{//尾删
assert(PSL);
assert(PSL->size > 0); //当有效数据为0,如果继续删除,报错
SLerase(PSL, PSL->size); //复用,删除最后一个元素
}
6、头删
void SLPopFront(SeqList* PSL)
{//头删
assert(PSL);
assert(PSL->size > 0); //当有效数据为0,如果继续删除,报错
SLerase(PSL, 1);
}
7、打印
void SLprintf(SeqList* PSL)
{//打印
assert(PSL);
for (int i = 0; i < PSL->size; i++)
{
printf("%d ", PSL->pa[i]);
}
printf("\n");
}
8、指定位置插入
void SLInsert(SeqList* PSL, int pos, SLdatetype x)
{//指定位置插入,插入的是第pos个位置,为x的数据
assert(PSL);
//对插入的位置进行判断,看是否越界
assert(pos >= 1 && pos <= PSL->size + 1);
CheckCapacity(PSL);
int end = PSL->size;
while (end > pos - 1)
{//pos是第几个数据
PSL->pa[end] = PSL->pa[end - 1];
end--;
}
PSL->pa[pos - 1] = x;
PSL->size++;
}
9、指定位置删除
void SLerase(SeqList* PSL, int pos)
{
assert(PSL);
//对删除的位置进行判断,看是否越界
assert(pos >= 1 && pos <= PSL->size);
int start = pos;
while (start < PSL->size)
{
PSL->pa[start - 1] = PSL->pa[start];
start++;
}
PSL->size--;
}
10、查找
int SLFind(SeqList* PSL, SLdatetype x)
{//查找
assert(PSL);
for (int i = 0; i < PSL->size; i++)
{
if (PSL->pa[i] == x)
{
return i;
}
}
return -1;
}
11、指定位置修改
void SLModify(SeqList* PSL, int pos, SLdatetype x)
{//指定位置修改
assert(PSL);
assert(pos >= 1 && pos <= PSL->size);
PSL->pa[pos - 1] = x;
}
五、源码
测试:
#include"SeqList.h"
void Menu()
{
printf("***********************************\n");
printf(" 1、头插 2、尾插\n");
printf(" 3、头删 4、尾删\n");
printf(" 5、打印 6、修改\n");
printf(" 7、查找 0、退出\n");
printf("***********************************\n");
}
void TestSeqList4()
{
int input = 0;
SeqList sl;
InItSeqList(&sl);
do
{
Menu();
printf("请选择要进行的操作:>");
//scanf("%d", &input);
input = getchar() - '0';
getchar();
int x = 0;
int pos = 0;
switch (input)
{
case 0:
printf("退出程序!");
break;
case 1:
printf("请输入要插入的数据个数,再依次输入:>");
int n = 0;
scanf("%d", &n);
while (n--)
{
scanf("%d", &x);
SLPushFront(&sl, x);
}
getchar();
break;
case 2:
printf("请输入要插入的数据个数,再依次输入:>");
int num = 0;
scanf("%d", &num);
while (num--)
{
scanf("%d", &x);
SLPushBack(&sl, x);
}
getchar();
break;
case 3:
SLPopFront(&sl);
printf("操作成功!\n");
break;
case 4:
SLPopBack(&sl);
printf("操作成功!\n");
break;
case 5:
SLprintf(&sl);
break;
case 6:
printf("请选择要修改第几个值,输入修改的目标值:>");
scanf("%d %d", &pos, &x);
SLModify(&sl, pos, x);
printf("操作成功!\n");
getchar();
break;
case 7:
printf("请输入需要查找的内容:>");
scanf("%d", &x);
int ret = SLFind(&sl, x);
if (ret == -1)
{
printf("查找失败!,查无此值\n");
}
else
{
printf("找到了,下标为%d\n", ret);
}
getchar();
break;
default:
printf("无此选项,请重新输入!\n");
break;
}
} while (input);
DestroySeqList(&sl);
}
int main()
{
TestSeqList4();
return 0;
}
声明:
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<assert.h>
//动态顺序表
typedef int SLdatetype;
typedef struct SepList
{
SLdatetype* pa;
int size; //有效元素个数
int capacity; //容量
}SeqList;
//初始化
void InItSeqList(SeqList* PSL);
//销毁
void DestroySeqList(SeqList* PSL);
//尾部插入
void SLPushBack(SeqList* PSL, SLdatetype x);
//头插
void SLPushFront(SeqList* PSL, SLdatetype x);
//尾删
void SLPopBack(SeqList* PSL);
//头删
void SLPopFront(SeqList* PSL);
//打印
void SLprintf(SeqList* PSL);
//指定位置插入
void SLInsert(SeqList* PSL, int pos, SLdatetype x);
//指定位置删除
void SLerase(SeqList* PSL, int pos);
//查找
int SLFind(SeqList* PSL, SLdatetype x);
//指定位置修改
void SLModify(SeqList* PSL, int pos, SLdatetype x);
定义:
#include"SeqList.h"
void InItSeqList(SeqList* PSL)
{
assert(PSL);
PSL->pa = (SLdatetype*)malloc(sizeof(SLdatetype) * 4);
if (PSL->pa == NULL)
{
perror("PSL->pa");
return;
}
PSL->size = 0;
PSL->capacity = 4;
}
void DestroySeqList(SeqList* PSL)
{
assert(PSL);
free(PSL->pa);
PSL->pa = NULL;
PSL->size = 0;
PSL->capacity = 0;
}
void CheckCapacity(SeqList* PSL)
{//检查是否越界
assert(PSL);
if (PSL->capacity == PSL->size)
{//容量耗尽扩容
SLdatetype* tmp = (SLdatetype*)realloc(PSL->pa, sizeof(SLdatetype) * PSL->capacity * 2);
if (tmp == NULL)
{
perror("realloc");
return;
}
PSL->pa = tmp;
PSL->capacity *= 2;
}
}
void SLprintf(SeqList* PSL)
{
assert(PSL);
for (int i = 0; i < PSL->size; i++)
{
printf("%d ", PSL->pa[i]);
}
printf("\n");
}
void SLPushBack(SeqList* PSL, SLdatetype x)
{//尾部插入
assert(PSL);
CheckCapacity(PSL);
//PSL->pa[PSL->size++] = x;
SLInsert(PSL, PSL->size + 1, x);
}
void SLPushFront(SeqList* PSL, SLdatetype x)
{//头插
assert(PSL);
CheckCapacity(PSL); //检查容量
//int end = PSL->size;
//while (end >= 0)
//{//每次插入前将数据依次向后挪动
// PSL->pa[end] = PSL->pa[end - 1];
// end--;
//}//也可以使用memmove
memmove(PSL->pa + 1, PSL->pa, PSL->date * sizeof(SLdatetype));
//PSL->pa[0] = x;
//PSL->size++;
SLInsert(PSL, 1, x); //复用SLInsert
}
void SLPopBack(SeqList* PSL)
{//尾删
assert(PSL);
assert(PSL->size > 0); //当有效数据为0,如果继续删除,报错
//PSL->size--; //直接将有效数据的个数较少,访问不到的数据相当于删除
SLerase(PSL, PSL->size);
}
void SLPopFront(SeqList* PSL)
{//头删
assert(PSL);
assert(PSL->size > 0); //当有效数据为0,如果继续删除,报错
//int start = 0;
//while (start < PSL->size - 1)
//{//将有效数据依次向前挪动,覆盖第一个数据
// PSL->pa[start] = PSL->pa[start + 1];
// start++;
//}
memmove(PSL->pa, PSL->pa + 1, (PSL->date - 1) * sizeof(SLdatetype));
//PSL->size--;
SLerase(PSL, 1);
}
void SLInsert(SeqList* PSL, int pos, SLdatetype x)
{//指定位置插入
assert(PSL);
//对插入的位置进行判断,看是否越界
assert(pos >= 1 && pos <= PSL->size + 1);
CheckCapacity(PSL);
int end = PSL->size;
while (end > pos - 1)
{//pos是第几个数据
PSL->pa[end] = PSL->pa[end - 1];
end--;
}
PSL->pa[pos - 1] = x;
PSL->size++;
}
void SLerase(SeqList* PSL, int pos)
{
assert(PSL);
//对删除的位置进行判断,看是否越界
assert(pos >= 1 && pos <= PSL->size);
int start = pos;
while (start < PSL->size)
{
PSL->pa[start - 1] = PSL->pa[start];
start++;
}
PSL->size--;
}
//查找
int SLFind(SeqList* PSL, SLdatetype x)
{
assert(PSL);
for (int i = 0; i < PSL->size; i++)
{
if (PSL->pa[i] == x)
{
return i;
}
}
return -1;
}
//指定位置修改
void SLModify(SeqList* PSL, int pos, SLdatetype x)
{
assert(PSL);
assert(pos >= 1 && pos <= PSL->size);
PSL->pa[pos - 1] = x;
}
记录一下对顺序表的简单应用。
但实际上,顺序表还是存在一些不足:
1、在中间或者头部进行插入删除操作时,时间复杂度为O(N)。
2、删除数据之后开辟的空间不能及时回收,有空间的浪费
3、增容一般呈两倍的增长,那就势必会有一定的空间会被浪费。例如当前空间为200,扩容后为400,再插入5个数据,后续没有数据了,就浪费了195个数据空间。
这些问题在线性表的下一个内容——链表,就可以得到解决。