数据结构-顺序表

文章目录

    • 线性表概念
    • 顺序表
      • 静态顺序表
      • 动态顺序表
    • 总结

线性表概念

线性表是最基本、最简单、也是最常用的一种数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…线性表(linear> list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
顺序表概念以及结构

![在这里插入图片描述](https://img-blog.csdnimg.cn/81fd4ce355c74003b39850991da432ee.png

顺序表

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。顺序表一般可以分为:
1.静态顺序表:使用定长数组存储。
2.动态顺序表:使用动态开辟的数组存储。

静态顺序表

概念:

静态顺序表就是给定存储数据的大小来存储数据,在运行时已经确定的了,一般都是通过一个固定大小的数组来储存元素

优点:

通过数组的下标就能够快速的找到相应的元素,有快速访问的效果

缺点

由于静态顺序表在开始时储存的空间大小已经确定了,无法改变,所以如果设置过大就会造成空间的浪费,如果太小就会造成数据溢出

实现:

1.我们通过静态的顺序表来实现增删
2.在实现我们的过程中每完成一个函数就验证一下,保证不会出错再实现下一个函数
3.我们要实现的函数有初始化、打印、判断数组空间是否已满、尾插、头插、尾删、头删

一.构建框架
1.通过多文件来链接

基本的头文件和创建的结构体和一些定义

#include<stdio.h>
#include<string.h>//用于初始化 memset函数
#include<stdlib.h>
#include <assert.h>//用assert函数-判断对错
#define  max 20   //方便改变数组的大小
typedef int SQDataType;//方便改变数据类型
struct SeqList     //创建结构体(由于结构体能放多种 不同的数据,
//我们这里就实现整数)
	{
		SQDataType a[max];//数组
		int size;//记录元素个数
	};
typedef  struct SeqList  sl;//简化

二.分模块实现
1.初始化
我们将结构体中的数组和元素个数都初始化为0
代码实现:

void SeqListInit(sl* ps) {
	memset(ps->a, 0, sizeof(SQDataType) * max);//将数组内容初始化为0
	ps->size=0;//开始拥有0个元素
}

检查:

2.判断数组是否已经满了
我们通过比较size(数组中元素的个数)和(max数组最大 容量)来判断
代码实现:

int man(sl* ps) {
	if (ps->size >= max)//当size大于max时此时数组空间已经满了
	{
		return 0;
	}
	else
		return 1;
}

3.尾插
因为数组是从下标0开始的,所以我们可以将插入的数据插到数组下标为size(元素的个数)那个位置即可,由于插入一个数据,所以size要加1
代码实现:

void  SeqListPushBack(sl* ps, SQDataType x) {//尾插
	assert(man(ps));//判断数组空间是否满了
	ps->a[ps->size] = x;//赋值
		ps->size++;//下标++
	}

检查:
我们这里插入 1,2,3,4


4.打印
通过循环将结构体中的数组内容打印出来
代码实现:

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

检查:

5.头插
头插过程中我们要通过循环将所有数据都往后移动一位,再减插入的元素放到下标为0的位置即可,当然size也要加1哦
代码实现:

void SeqListPushFront(sl* ps, SQDataType x) {//头插
	assert(man(ps));
	for (int i =ps->size; i>0; i--) {//所有数据后移动一位
		ps->a[i] =ps->a[i-1] ;
	}
	ps->a[0] = x;
	ps->size++;
}

检查
我们在上面的基础上头插一个0


6.尾删
我们可以直接将数组最后一个赋为0(因为我们初始化本身就是0),当然size要减1
代码实现:

void SeqListPopBack(sl* ps) {//尾删
	ps->a[ps->size - 1] = 0;
	ps->size--;
}

检查:
在上面的基础上减去两个数


7.头删
我们可以直接通过循环直接将首元素直接覆盖
代码实现:

void SeqListPopFront(sl* ps) {//头删

	for (int i = 0; i < ps->size; i++)//所有数据前移动一位,将头个数据给覆盖掉
		ps->a[i] = ps->a[i + 1];
	    ps->size--;//减少一个元素
}

检查:
在上面的基础上头删两个数据


所有代码一览:
SeqList.h:


```c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>//用于初始化 memset函数
#include<stdlib.h>
#include <assert.h>//用assert函数-判断对错
#define  max 20   //方便改变数组的大小
typedef int SQDataType;//方便改变数据类型
struct SeqList     //创建结构体
	{
		SQDataType a[max];
		int size;//记录元素个数
	};
typedef  struct SeqList  sl;//简化
// 增删查改等接口函数
void SeqListInit(sl* ps);//初始化
int man(sl* ps);//判断数组是否已满
void SeqListPrint(sl* ps);//打印
//   尾插  头插 尾删  头删
void SeqListPushBack(sl* ps, SQDataType x);//尾插
void SeqListPushFront(sl* ps, SQDataType x);//头插
void SeqListPopBack(sl* ps);//尾删
void SeqListPopFront(sl *ps);//头删

SeqList.c:

#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
void SeqListInit(sl* ps) {
	memset(ps->a, 0, sizeof(SQDataType) * max);//将数组内容初始化为0
	ps->size=0;//开始拥有0个元素
}
void SeqListPrint(sl* ps) {//打印
	for (int i = 0; i < ps->size; i++)
		printf("%d ", ps->a[i]);
	printf("\n");
}
int man(sl* ps) {
	if (ps->size >= max)//当size大于max时此时数组空间已经满了
	{
		return 0;
	}
	else
		return 1;
}

void  SeqListPushBack(sl* ps, SQDataType x) {//尾插
	assert(man(ps));//判断数组空间是否满了
	ps->a[ps->size] = x;//赋值
		ps->size++;//下标++
	}

void SeqListPushFront(sl* ps, SQDataType x) {//头插
	assert(man(ps));
	for (int i =ps->size; i>0; i--) {//所有数据后移动一位
		ps->a[i] =ps->a[i-1] ;
	}
	ps->a[0] = x;
	ps->size++;
}
void SeqListPopBack(sl* ps) {//尾删
	ps->a[ps->size - 1] = 0;
	ps->size--;
}
void SeqListPopFront(sl* ps) {//头删

	for (int i = 0; i < ps->size; i++)//所有数据前移动一位,将头个数据给覆盖掉
		ps->a[i] = ps->a[i + 1];
	    ps->size--;//减少一个元素
}

Test.c:

#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
void teat1() {
	sl s;
	SeqListInit(&s);
	SeqListPushBack(&s, 1);
	SeqListPushBack(&s, 2);
	SeqListPushBack(&s, 3);
	SeqListPushBack(&s, 4);
	
	SeqListPushFront(&s, 0);
	SeqListPopBack(&s);
	SeqListPopBack(&s);
	SeqListPopFront(&s);
	SeqListPopFront(&s);
	SeqListPrint(&s);
}
int main() {
	teat1();
	return 0;
}

以上就是静态顺序表的增删实现了

动态顺序表

概念

动态顺序表可以根据需求来扩容存储空间,比静态顺序表灵活,因此动态顺序表被广泛使用

优点:

可以根据需要动态地分配内存,灵活性更高。 可以避免静态数组可能出现的内存浪费问题。
由于内存空间是动态分配的,因此可以处理数据量不确定或者变化的情况

缺点:

由于插入、删除数据时可能会移动大量数据,所以效率较低

实现:
动态顺序表和静态的过程差不多,就是将数组的空间大小改为可变的
1.在顺序表的基础上增加了扩容函数,不需要判断空间是否满的函数,初始化函数也改变了
2.用指针的方式将数据链接起来
一.改变的创建的结构体

struct SeqList {
	SQDataType* a;//用指针的方式将数据链接起来
	int size;//元素的个数
    int capacity;//空间的大小
};

二.主要函数的实现
1.初始化:

void SeqListInit(sl* ps) {
	ps->a = NULL;//将指针置空
	ps->size = 0;//开始为0
	ps->capacity = 0;//空间开始为0
}

检查:

2.扩容:

void SeqListCheckCapacity(sl* ps) {
	if (ps->size == ps->capacity) {//判断空间是否满了,满了就扩容
		int ws = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		//如果空间为0就先给4不然就给2倍
		SQDataType* tmp = (SQDataType*)realloc(ps->a, sizeof(SQDataType) * ws);//扩容,将首地址给tmp

		if (tmp == NULL)//判断是否成功扩容
		{
			printf("realloc fail\n");
			exit(-1);
		}
		else
		{
			ps->a = tmp;//将首地址给a
			ps->capacity = ws;//将扩容后的空间大小赋给capacity
		}
	}
}

3.尾插
因为数组是从下标0开始的,所以我们可以将插入的数据插到数组下标为size(元素的个数)那个位置即可,由于插入一个数据,所以size要加1
代码实现:

void  SeqListPushBack(sl* ps, SQDataType x) {//尾插
	SeqListCheckCapacity(ps);//判断是否要扩容
	ps->a[ps->size] = x;//赋值
		ps->size++;//下标++
	}

检查:
我们这里插入 1,2,3,4

![在这里插入图片描述](https://img-blog.csdnimg.cn/dc0edc5f9ed444249653ea0805c7403a.png

4.打印与静态顺序表一样
5.头插
头插过程中我们要通过循环将所有数据都往后移动一位,再减插入的元素放到下标为0的位置即可,当然size也要加1哦
代码实现:

void SeqListPushFront(sl* ps, SQDataType x) {//头插
	SeqListCheckCapacity(ps);
	for (int i =ps->size; i>0; i--) {//所有数据后移动一位
		ps->a[i] =ps->a[i-1] ;
	}
	ps->a[0] = x;
	ps->size++;
}

检查:
我们在上面的基础上头插一个0


6.由于尾删和头删没有涉及到扩容,所以和静态顺序表的一样
代码一览:
SeqList.h:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>//用于初始化 memset函数
#include<stdlib.h>
#include <assert.h>//用assert函数-判断对
struct SeqList {
	SQDataType* a;
	int size;
    int capacity;
};
typedef  struct SeqList  sl;//简化
// 增删查改等接口函数
void SeqListInit(sl* ps);//初始化
//int man(sl* ps);//判断数组是否已满
void SeqListCheckCapacity(sl* ps);//扩容函数
void SeqListPrint(sl* ps);//打印
//   尾插  头插 尾删  头删
void SeqListPushBack(sl* ps, SQDataType x);//尾插
void SeqListPushFront(sl* ps, SQDataType x);//头插
void SeqListPopBack(sl* ps);//尾删
void SeqListPopFront(sl *ps);//头删

SeqList.c

void SeqListInit(sl* ps) {
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}
void SeqListPrint(sl* ps) {//打印
	for (int i = 0; i < ps->size; i++)
		printf("%d ", ps->a[i]);
	printf("\n");
}
void SeqListCheckCapacity(sl* ps) {
	if (ps->size == ps->capacity) {
		int ws = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SQDataType* tmp = (SQDataType*)realloc(ps->a, sizeof(SQDataType) * ws);

		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		else
		{
			ps->a = tmp;
			ps->capacity = ws;
		}
	}
}

void  SeqListPushBack(sl* ps, SQDataType x) {//尾插
	SeqListCheckCapacity(ps);
	ps->a[ps->size] = x;//赋值
		ps->size++;//下标++
	}

void SeqListPushFront(sl* ps, SQDataType x) {//头插
	SeqListCheckCapacity(ps);
	for (int i =ps->size; i>0; i--) {//所有数据后移动一位
		ps->a[i] =ps->a[i-1] ;
	}
	ps->a[0] = x;
	ps->size++;
}
void SeqListPopBack(sl* ps) {//尾删
	ps->a[ps->size - 1] = 0;
	ps->size--;
}
void SeqListPopFront(sl* ps) {//头删

	for (int i = 0; i < ps->size; i++)//所有数据前移动一位,将头个数据给覆盖掉
		ps->a[i] = ps->a[i + 1];
	    ps->size--;//减少一个元素
	    }

Tset.c;

#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
void teat1() {
	sl s;
	SeqListInit(&s);
	SeqListPushBack(&s, 1);
	SeqListPushBack(&s, 2);
	SeqListPushBack(&s, 3);
	SeqListPushBack(&s, 4);
	
	//SeqListPushFront(&s, 0);
	//SeqListPopBack(&s);
	//SeqListPopBack(&s);
	//SeqListPopFront(&s);
	//SeqListPopFront(&s);
	SeqListPrint(&s);
}
int main() {
	teat1();
	return 0;
}

总结

1.还有查改等没写,有兴趣的可以去试试哦
2.顺序表虽然可以通过扩容来解决空间问题,但是每次以2倍的增长时,还是会造成一定的空间浪费,如:我扩了100个空间,我只用了10个。
3.就是顺序表在插入或者删除时,需要移动大量的数据,这样导致效率不高

以上就是我的分享了,如果有什么错误,欢迎在评论区留言。
最后,谢谢大家的观看!

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2023年12月8日
下一篇 2023年12月8日

相关推荐