顺序表详解(最后包含完整且成功测试的顺序表~需要可自取)

目录

1.顺序表概念

1.顺序表定义

2.顺序表的分类

1.静态顺序表

2.动态顺序表

3.实现顺序表时函数的命名规则

2.实现顺序表最基本的函数

1.顺序表的结构体定义

2.打印顺序表

3.判断顺序表是否已经满

4.顺序表的销毁

3.尾插与尾删

1.尾插

2.尾删

4.头插与头删

1.头插

2.头删

5.在特定位置插入或删除数据

1.在pos处插入数据

2.在pos处删除数据

6.查找数据

7.全部代码即测试结果

1.seq.h文件

2.seq.c文件

3.test.c文件

4.测试结果


1.顺序表概念

1.顺序表定义

用数组进行存储,数据从头逐个向后进行存储。

2.顺序表的分类

1.静态顺序表

这种顺序表直接定义了数组的大小,当数组填满时,不能继续增加元素,我们在存储数据的时候并不一定知道要存储多少数据,所以并不满足我们的要求。

2.动态顺序表

这种顺序表在定义的时候通常会使用指针来进行标记数据,通过移动指针来进行数据的增加和删除。动态顺序表比静态顺序表更加常用。也是我们要讨论的内容。

3.实现顺序表时函数的命名规则

函数的命名规则还是要根据驼峰规则的,起名的方式参考C++中STL库中函数的命名,方便记忆其中函数的功能。

2.实现顺序表最基本的函数

1.顺序表的结构体定义

typedef struct SeqList
{
int* a;//定义一个指向数组中元素的指针
int size;//定义当前数组的大小
int capacity;//定义数组的最大容量
}

2.打印顺序表

SeqListPrint(SL* ps)
{
int i;
for(i=0;i<ps->size;++i)
{
printf("%d",ps->a[i]);
}
printf("\n");
}

使i从第一个元素开始,向后遍历并打印。

3.判断顺序表是否已经满

void SeqListCheckCapacity(SL* ps,SLDataType x)
{
int newcapacity=ps->capacity==0?4:ps->capacity*2;//建立newcapacity使之为原来的二倍
if(ps->size==ps->capacity)
{
SLDataType* tmp=(SLDataType*)realloc(ps->a,newcapacity* sizeof(SLDataType));//扩充 
                                                            newcapacity个数据的空间。
if(tmp==NULL)
{
printf("realloc fail");//扩充失败程序异常退出
exit(-1);
}
ps->a=tmp;接收扩充后的空间
ps->capacity=newcapacity;//将数组容量扩充为原来的二倍
}

当ps->size与ps->capacity相等的时候,说明顺序表已满,我们就需要对顺序表进行扩容,当进行第一次扩容的时候,ps->capacity是表示0的,我们先为它开辟四个字节的空间,在之后的扩容中,我们开辟的空间为原来空间的二倍大小。

在扩容过程中使用的是动态内存分配,即再开辟newcapacity大小的空间。当扩容失败时,realloc返回的是一个空指针,所以当tmp为空指针的时候扩容失败,exit(-1)异常退出程序。

最后返回新内存的首元素地址(和原来的一样),和新的capacity的值。

4.顺序表的销毁

void SeqListDestroy(SL* ps)
{
free(ps->a);//释放动态内存开辟的空间
pa->a=NULL;
pa->capacity=ps->size=0;
}

当我们要销毁顺序表的时候,我们需要首先释放空间,此时顺序表的头指针变成了一个野指针,我们需要将它赋值为NULL,然后将ps->capacity和ps->size赋值为0。

3.尾插与尾删

尾插与尾删,即在顺序表的末尾处对顺序表进行插入或者删除。

1.尾插

void SeqListPushBack(SL* ps,SLDataType x)
{
SeqListCheckCapacity(SL* ps);//检测顺序表是否已满
ps->a[pa->size]=x;
ps->size++;
}

当进行尾插时,首先把size的位置赋值为要插入的数据,然后再对size++,再插入数据,依次类推。

2.尾删

void SeqListPopback(SL* ps)
{
assert(ps->size>0);//当ps->size不大于0时程序报错退出
ps->size--;
}

当进行尾删的时候,只需要对size进行--操作就可以,当进行尾删之前,需要判断ps->size是否大于0,当ps->size==0的时候,说明顺序表中已经没有数据可以进行删除了,不能再对size进行--操作了。

4.头插与头删

即在顺序表的头处对顺序表进行插入或者删除。

1.头插

void SeqListPushFront(SL* ps,SLDataType 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++;
}

在顺序表头处进行插入数据,即需要将顺序表原有的数据同时向后移动一位,然后将要插入的数据进行插入,再将size++即可。

2.头删

void SeqListPopFront(SL* ps)
{
assert(ps->size>0);//断言判断顺序表是否为空
int begin=1;
while(begin<ps->size)
{
ps->a[begin-1]=ps->a[begin];//用后一个数据覆盖前一个数据
++begin;
}
ps->size--;
}

在进行头删的时候,首先需要判断顺序表是否为空,即使用断言语句assert(ps->size>0)当顺序表为空的时候,程序会报错。删除元素即将之后的所有元素逐个向前移动一位,将第一个元素进行覆盖即可,然后再进行size--。

5.在特定位置插入或删除数据

1.在pos处插入数据

void SeqListInsert(SL* ps;int pos;SLDataType)
{
assert(pos<=ps->size&&ps>=0);//判断pos位置是否合法
SeqListCheckCapacity(ps);//判断ps是否已满,已满则扩容
int end=ps->size-1;
while(end>=pos)
{
ps->a[end+1]=ps->a[end];//用从pos开始前一个数据覆盖后一个数据
--end;
}
ps->a[pos]=x;
ps->size++;
}

在向pos处插入数据的时候,首先需要判断pos是否合法,然后再检查顺序表是否已满,如果满了就要进行扩容,因此调用了SeqListCheckCapacity函数,之后将原来pos以及pos之后的元素都向后移动一位,再将x插入到pos的位置。

2.在pos处删除数据

void SeqListErase(SL* ps,int pos)
{
assert(pos>=0&&pos<ps->size);//判断pos是否合法
int begin=pos+1;
while(begin<ps->size)
{
ps->a[begin-1]=ps->a[begin];//从pos开始用后一个数据覆盖前一个数据
++begin;
}
ps->size--;
} 

在pos处删除数据,首先判断pos是否合法,然后将pos之后的数据都向前移动一位,再将size--。

6.查找数据

int SeqListFind(SL*ps,SLDataType x)
{
int i;
for(i=0;i<ps->size;i++)
{
if(ps->a[i]==x)
{
return i;
}
}
return -1;//没找到返回-1
}

从开始向后查找,如果找到返回下标,没找到则返回-1。

7.全部代码即测试结果

1.seq.h文件

#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#define SLDataType int
typedef struct List
{
	SLDataType* a;
	int size;
	int capacity;
}SL;
void SeqListInit(SL* ps);
void SeqListDestroy(SL* ps);
void SeqListPrint(SL* ps);
void SeqListCheckCapacity(SL* ps);
void SeqListPushBack(SL* ps, SLDataType x);
void SeqListPopBack(SL* ps);
void SeqListPushFront(SL* ps, SLDataType x);
void SeqListPopFront(SL* ps);
void SeqListInsert(SL* ps, int pos, SLDataType x);
void SeqListErase(SL* ps, int pos);
int SeqListFind(SL* ps, SLDataType x);

2.seq.c文件

#include"seq.h"
void SeqListInit(SL* ps)//顺序表初始化
{
	ps->a = NULL;
	ps->size = ps->capacity = 0;
}
void SeqListDestroy(SL* ps)//顺序表销毁
{
	free(ps->a);
	ps->size = ps->capacity = 0;
}
void SeqListPrint(SL* ps)//顺序表打印
{
	int i;
	for (i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}
void SeqListCheckCapacity(SL* ps)//顺序表检查是否已满,已满则扩容
{
	if (ps->size == ps->capacity)
	{
		int newcapacity = (ps->capacity == 0 ? 4 : ps->capacity * 2);
		SLDataType* temp = (SLDataType*)realloc(ps->a, newcapacity*sizeof(SLDataType));
		if (temp == NULL)
		{
			printf("realloc failed\n");
			exit(-1);
		}
		ps->a = temp;
		ps->capacity = newcapacity;
	}
}
void SeqListPushBack(SL* ps, SLDataType x)//顺序表尾插
{
	SeqListCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}
void SeqListPopBack(SL* ps)//顺序表尾删
{
	assert(ps->size > 0);
	ps->size--;
}
void SeqListPushFront(SL* ps, SLDataType x)//顺序表头插
{
	SeqListCheckCapacity(ps);
	int end = ps->size-1;
	int i;
	for (i = 0; i < ps->size; i++)
	{
		ps->a[end+1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}
void SeqListPopFront(SL* ps)//顺序表头删
{
	assert(ps->size > 0);
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}
void SeqListInsert(SL* ps, int pos, SLDataType x)//顺序表任意插入
{
	assert(pos >= 0 && 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 SeqListErase(SL* ps, int pos)//顺序表任意删除
{
	assert(pos >= 0 && pos < ps->size);
	int begin = pos+1;
	while (begin < ps->size-1)
	{
		ps->a[begin-1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}
int SeqListFind(SL* ps, SLDataType x)//查找
{
	int i;
	for (i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}

3.test.c文件

#include"seq.h"
void menu()
{
	printf("***********************\n");
	printf("**1.尾插     2.尾删****\n");
	printf("**3.头插     4.头删****\n");
	printf("**5.固定插入 6.固定删除\n");
	printf("**7.查找     0.删除顺序表\n");
	printf("**0.销毁并退出*********\n");
}
int main()
{
	SL ps;
	SeqListInit(&ps);
	SeqListPushBack(&ps, 4);
	SeqListPushBack(&ps, 5);
	SeqListPushBack(&ps, 6);
	SeqListPushFront(&ps, 3);
	SeqListPushFront(&ps, 2);
	SeqListPushFront(&ps, 1);
	SeqListPrint(&ps);//顺序表初始化
	int input;
	do 
	{
		menu();
		printf("please input your choice:");
		scanf("%d", &input);
		SLDataType x;
		int pos;
		int find;
		int getfind;
		switch (input)//顺序表输入测试
		{
		case 0:
			SeqListDestroy(&ps);
			break;
		case 1:
			scanf("%d", &x);
			SeqListPushBack(&ps, x);
			SeqListPrint(&ps);
			break;
		case 2:
			SeqListPopBack(&ps);
			SeqListPrint(&ps);
			break;
		case 3:
			scanf("%d", &x);
			SeqListPushFront(&ps, x);
			SeqListPrint(&ps);
			break;
		case 4:
			SeqListPopFront(&ps);
			SeqListPrint(&ps);
			break;
		case 5:
			scanf("%d", &pos);
			scanf("%d", &x);
			SeqListInsert(&ps, pos, x);
			SeqListPrint(&ps);
			break;
		case 6:
			scanf("%d", &pos);
			SeqListErase(&ps, pos);
			SeqListPrint(&ps);
			break;
		case 7:
			scanf("%d", &find);
			getfind = SeqListFind(&ps, find);
			printf("%d", getfind);
			printf("\n");
			break;
		default:
			printf("error input\n");
			break;
		}
	} while (input);
	return 0;
}

4.测试结果

1.尾插:测试成功

2.尾删:尾删至没有元素,程序退出,成功

3.头插:测试成功

4.头删:头删至没有元素,程序退出,成功

5.任意输入:进行了边缘检测和普通检测,成功

6.任意删除:进行了普通检测和越界检测,成功

7.查找位置:找到返回下标,没找到返回-1,成功

8.退出程序:测试成功

综上,程序测试成功!

8.总结

顺序表是学习数据结构的第一章节,其优点和缺点也是很明显的,由于是数据的连续存储所以访问速度极快,但数据的插入和删除就没有链表的方便,操作起来没有链表那样灵活,而且会造成内存分块过多,算是以空间换取时间的代表。

posted @   卖寂寞的小男孩  阅读(277)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示