我们今天学习顺序表,众所周知,顺序表分为静态顺序表和动态顺序表,让我们来看一下吧
目录
1.线性表
1.概念:线性表是个具有相同特性的数据元素的有限序列,它是一种在实际中广泛应用的数 据结构。
2.常见类型:顺序表、链表、栈、队列、字符串(我们之后都要逐个学习)
3.线性表在逻辑上是线性的,也就是说是连续的一条直线。但在物理结构上不一定连续。
4.线性表在物理结构上存储时,通常以数组和链式的结构形式存储。(我们下一篇博客学习链式结构)。
2.顺序表的实现
顺序表是一段物理地址连续的存储单元,它是一种依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成数据的增删查改等操作。
顺序表一般分为:
1.静态顺序表:使用定长数组存储,它的空间是固定的,用完就没有了,不推荐使用
2.动态顺序表:使用动态开辟的数组存储,只要电脑有空间,就可以无限realloc出来,推荐
接下来,让我们看看它们如何实现。
2.1静态顺序表的实现
我们一般认为静态顺序链表是无意义的,因为它的空间是固定的,用完就越界了,空间给少了不够用,给多了会造成空间的浪费,十分笨拙,但在这里我还是给大家实现了,上代码
SList.h文件
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#define MAX_SIZE 10
typedef int SADataType;
//创建一个结构体用来表示顺序表
typedef struct SListNode
{
SADataType a[MAX_SIZE];//数组,用来存放数据
int size;//已存入数据的个数
}SLNode;
//初始化顺序表
void SListInit(SLNode* ps);
//在顺序表中头插入数据
void SListPushFront(SLNode* ps,SADataType x);
//在顺序表中尾插数据
void SListPushBack(SLNode* ps, SADataType x);
//打印顺序表
void SListPrint(SLNode* ps);
//顺序表的销毁
void SListDestory(SLNode* ps);
//在顺序表中头删数据
void SListPopFront(SLNode* ps);
//在顺序表中尾删数据
void SListPopBack(SLNode* ps);
//在顺序表中查找指定的数据
SADataType SListSearch(SLNode* ps, SADataType x);
//修改这个查找到的数据
void SListModify(SLNode* ps, SADataType pos, SADataType data);
//任意位置的插入,这个任意位置指的是被查找到的位置,即pos的位置
void SListInsert(SLNode* ps, SADataType pos, SADataType x);
//任意位置的删除
void SListErase(SLNode* ps, SADataType pos);
SList.c文件
#include"SList.h"
//初始化顺序表
void SListInit(SLNode* ps)
{
ps->size = 0;
memset(ps->a, 0,sizeof(SADataType)* MAX_SIZE);
}
//在顺序表中头插入数据
void SListPushFront(SLNode* ps, SADataType x)
{
//判断顺序表是否已满,如果满了,直接退出程序
//要头插,先把数组中的数据向后挪1,然后在头部插入,所以要判断尾部是否有空余的空间
if (ps->size >= MAX_SIZE )
{
printf("SList is FULL!\n");
return;
}
//没有满,开始插入数据
int i = 0;
//向后挪
for (i = ps->size-1; i>=0; i--)
{
ps->a[i+1] = ps->a[i];
}
//空出头,开始插入
ps->a[0] = x;
ps->size++;
}
//在顺序表中尾插数据
void SListPushBack(SLNode* ps, SADataType x)
{
//判断顺序表是否已满
if (ps->size >= MAX_SIZE)
{
printf("SList is FULL!\n");
return;
}
//开始插入数据
ps->a[ps->size] = x;
ps->size++;
}
//打印顺序表
void SListPrint(SLNode* ps)
{
if (ps->size == 0)
{
printf("SList is NULL!\n");
return;
}
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf(" %d ", ps->a[i]);
}
printf("\n");
}
//顺序表的销毁
void SListDestory(SLNode* ps)
{
if (ps->size == 0)
{
printf("SList is NULL!\n");
return;
}
//直接置为0
int i = 0;
for (i = 0; i < ps->size; i++)
{
ps->a[i] = 0;
}
ps->size = 0;
}
//在顺序表中头删数据
void SListPopFront(SLNode* ps)
{
//删除数组中的数据,只需要覆盖就可以,让后面的数据向前覆盖
int i = 0;
for (i = 0; i < ps->size; i++)
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
//在顺序表中尾删数据
void SListPopBack(SLNode* ps)
{
//尾删,直接让size--就可以
ps->size--;
}
//在顺序表中查找指定的数据
SADataType SListSearch(SLNode* ps, SADataType x)
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;//返回它的下标位置
}
}
return 0;
}
//修改这个查找到的数据
void SListModify(SLNode* ps, SADataType pos, SADataType data)
{
//在查找到的基础上,直接替换
ps->a[pos] = data;
}
//任意位置的插入,这个任意位置指的是被查找到的位置,即pos的位置
void SListInsert(SLNode* ps, SADataType pos, SADataType x)
{
if (ps->size >= MAX_SIZE)
{
printf("SList is FULL!\n");
return;
}
//先把pos后挪一个空位出来,在插入数据
int i = 0;
for (i = ps->size-1; i >= pos; i--)
{
ps->a[i + 1] = ps->a[i];
}
ps->a[pos] = x;
ps->size++;
}
//任意位置的删除
void SListErase(SLNode* ps, SADataType pos)
{
//直接向前覆盖
int i = 0;
for (i = pos; i < ps->size; i++)
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
test.c文件
#include"SList.h"
void Test1()
{
SLNode s;
SListInit(&s);
//尾插数据
SListPushBack(&s, 4);
SListPushBack(&s, 5);
SListPushBack(&s, 6);
SListPushBack(&s, 7);
SListPushBack(&s, 8);
SListPushBack(&s, 9);
SListPrint(&s);
//头插数据
SListPushFront(&s, 3);
SListPushFront(&s, 2);
SListPushFront(&s, 1);
SListPushFront(&s, 0);
SListPrint(&s);
//开始查找指定数据
SADataType pos = SListSearch(&s, 7);
if (pos==0)
{
printf("NOT Find!\n");
}
printf("The pos is %d \n", pos);
SListModify(&s, pos, 77);
SListPrint(&s);
//开始头删尾删
SListPopBack(&s);
SListPopBack(&s);
SListPopFront(&s);
SListPrint(&s);
//在pos后插入数据
SListInsert(&s, pos, 8);
SListInsert(&s, pos, 9);
SListInsert(&s, pos, 10);
SListPrint(&s);
SListErase(&s,pos);
SListErase(&s, pos);
SListPrint(&s);
SListDestory(&s);
printf("SList is Destory!\n");
}
int main()
{
Test1();
return 0;
}
结果:大家可以自己对照着结果查看
2.2动态顺序表的实现
动态顺序表相比于静态的顺序表,它更加的灵活,空间大小不受限制,容量不够就增容,一般不会造成越界,有了静态表的基础,动态顺序表也直接上代码
SeqList.h文件
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int AnyDataT;//对它进行类型重定义,后续想更换别的类型也很方便
//创建一个结构体,用来存放顺序表的相关信息
typedef struct seqList
{
AnyDataT* a;//写成指针,可以动态开辟内存,数组不可以,数组是静态的
int size;//顺序表中总共的数据
int capacity;//空间容量
}SL;
//初始化顺序表
void InitSeqList(SL* ps);
//检查容量,如果容量不够,我们动态开辟一块空间
void SeqListCheckCapacity(SL* ps);
//打印顺序表
void SeqListPrint(SL* ps);
//在顺序表头部插入一个数据
void seqListPushFront(SL* ps, AnyDataT x);
//在顺序表尾部插入一个数据
void seqListPushBack(SL* ps, AnyDataT x);
//删除顺序表头部的数据
void seqListPopFront(SL* ps);
//删除顺序表尾部的数据
void seqListPopBack(SL* ps);
//随机位置插入一个数据
void ranInsertSeqList(SL* ps,int pos,AnyDataT x);
//随机删除一个数据
void rDestorySeqList(SL* ps, int pos);
//查找顺序表中的数据
int seqListFind(SL* ps, AnyDataT x);
//修改顺序表中某一个位置的数据
//void modifySeqList(SL* ps, AnyDataT* x);
void modifySeqList(SL* ps, int pos, AnyDataT x);
//销毁顺序表
void destorySeqList(SL* ps);
SeqList.c文件
#include"SeqList.h"
//初始化顺序表
void InitSeqList(SL* ps)
{
ps->a = NULL;//初始化指针置为空
ps->size = 0;
ps->capacity = 0;//初始化空间为0,后续我们动态开辟
}
//检查容量,如果容量不够,我们动态开辟一块空间
void SeqListCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
AnyDataT* tmp = (AnyDataT*)realloc(ps->a, sizeof(AnyDataT) * newcapacity);
if (tmp == NULL)
{
perror("realloc");
return;
}
ps->a = tmp;
ps->capacity = newcapacity;
}
}
//打印顺序表
void SeqListPrint(SL* ps)
{
assert(ps->size > 0);
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
//在顺序表头部插入一个数据
void seqListPushFront(SL* ps, AnyDataT x)
{
SeqListCheckCapacity(ps);
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[0] = x;
ps->size++;
}
//在顺序表尾部插入一个数据
void seqListPushBack(SL* ps, AnyDataT x)
{
SeqListCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
//删除顺序表头部的数据
void seqListPopFront(SL* ps)
{
//先判断顺序表是否为空,为空则无法删除数据,我们直接用断言
assert(ps->size > 0);
int start = 1;//直接从下标1开始前挪
while (start < ps->size)
{
ps->a[start-1] = ps->a[start];
start++;
}
ps->size--;
}
//删除顺序表尾部的数据
void seqListPopBack(SL* ps)
{
assert(ps->size > 0);
ps->size--;
}
//随机位置插入一个数据,pos指下标
void ranInsertSeqList(SL* ps, int pos, AnyDataT x)
{
assert(pos < ps->size);
SeqListCheckCapacity(ps);
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[pos] = x;
ps->size++;
}
//随机删除一个数据
void rDestorySeqList(SL* ps, int pos)
{
assert(pos < ps->size);
int start = pos + 1;
while (start < ps->size)
{
ps->a[start - 1] = ps->a[start];
start++;
}
ps->size--;
}
//销毁顺序表
void destorySeqList(SL* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
//查找顺序表中的数据,我们诶个遍历
int seqListFind(SL* ps, AnyDataT x)
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
return ps->a[i];
}
printf("没有这个数据\n");
return 0;
}
void modifySeqList(SL* ps, int pos, AnyDataT x)
{
assert(pos < ps->size);
ps->a[pos] = x;
}
test.c文件
#include"SeqList.h"
//实现动态顺序表,
//我们实现的顺序表,一次都只插入一个数据,若要插入多个,还需仔细分析容量问题
//加入枚举,给Switch case语句添加一个提示
enum SeqList
{
ExitSeqlist,
SeqListPushFront,
SeqListPushBack,
SeqListPopFront,
SeqListPopBack,
RanInsertSeqList,
RanDestorySeqList,
ModifySeqList,
DestorySeqList,
SeqListFind,
};
void menu()
{
printf("******************************************************\n");
printf("******** 1.SeqListPushFront *******\n");
printf("******** 2.SeqListPushBack *******\n");
printf("******** 3.SeqListPopFront *******\n");
printf("******** 4.SeqListPopBack *******\n");
printf("******** 5.RanInsertSeqList *******\n");
printf("******** 6.RanDestorySeqList *******\n");
printf("******** 7.ModifySeqList *******\n");
printf("******** 8.DestorySeqList *******\n");
printf("******** 9.SeqListFind *******\n");
printf("******** 0.ExitSeqlist *******\n");
printf("******************************************************\n");
}
int main()
{
int input = 0;
SL s;//创建一个结构体变量
AnyDataT x = 0;
int pos = 0;
InitSeqList(&s);//初始化结构体
do
{
menu();
printf("请输入你要选择的操作:>");
scanf("%d", &input);
switch (input)
{
case SeqListPushFront:
printf("输入你要在头部插入的数字:>");
scanf("%d", &x);
seqListPushFront(&s, x);
SeqListPrint(&s);
break;
case SeqListPushBack:
printf("输入你要在尾部插入的数字:>");
scanf("%d", &x);
seqListPushBack(&s, x);
SeqListPrint(&s);
break;
case SeqListPopFront:
seqListPopFront(&s);
SeqListPrint(&s);
break;
case SeqListPopBack:
seqListPopBack(&s);
SeqListPrint(&s);
break;
case RanInsertSeqList:
printf("请输入要插入的下标位置和数据:>");
scanf("%d%d", &pos, &x);
ranInsertSeqList(&s, pos, x);
SeqListPrint(&s);
break;
case RanDestorySeqList:
printf("请输入要删除数据的下标位置:>");
scanf("%d", &pos);
rDestorySeqList(&s,pos);
SeqListPrint(&s);
break;
case ModifySeqList:
printf("请输入要修改位置的下标和修改后的数据:>");
scanf("%d%d", &pos, &x);
modifySeqList(&s,pos,x);
SeqListPrint(&s);
break;
case DestorySeqList:
destorySeqList(&s);
printf("顺序表已销毁\n");
break;
case SeqListFind:
printf("请输入要查找的数据:>");
scanf("%d", &x);
int data=seqListFind(&s, x);
printf("找到了%d这个数据\n", data);
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
结果:大家可以自己往里输入,我在这里做了一个简易的菜单呢,但在之后的链表实现中,为了测试方便,不会做菜单了,只做测试用例。
这就是我们这篇博客的所有内容了,下篇博客会带大家了解链表。
相信大家的品读,我们下期再见!!!