[初阶数据结构】顺序表

前言

📚作者简介:爱编程的小马,正在学习C/C++,Linux及MySQL。

📚本文收录于初阶数据结构系列,本专栏主要是针对时间、空间复杂度,顺序表和链表、栈和队列、二叉树以及各类排序算法,持续更新!

📚相关专栏C++及Linux正在发展,敬请期待!

目录

前言

[1. 顺序表](#1. 顺序表)

[1.1 概念及结构](#1.1 概念及结构)

[1.2 接口实现](#1.2 接口实现)

[1.2.1 动态顺序表的初始化](#1.2.1 动态顺序表的初始化)

[1.2.2 销毁顺序表](#1.2.2 销毁顺序表)

[1.2.3 顺序表的打印](#1.2.3 顺序表的打印)

[1.2.4 顺序表动态开辟空间容量的检测](#1.2.4 顺序表动态开辟空间容量的检测)

[1.2.5 往顺序表中插入数据](#1.2.5 往顺序表中插入数据)

[1.2.6 判断数字是否在顺序表中存在](#1.2.6 判断数字是否在顺序表中存在)

[1.2.7 顺序表删除数据](#1.2.7 顺序表删除数据)

[1.2.8 查找数字](#1.2.8 查找数字)

[1.2.9 修改顺序表中的数](#1.2.9 修改顺序表中的数)

二、完整的顺序表代码

1.test.c主函数代码

2.seqlist.h主函数声明代码

[3. seqlist.c函数实现代码](#3. seqlist.c函数实现代码)

总结


1. 顺序表

1.1 概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数据存储。在数组上完成数据的增删查改。

顺序表一般可以分为:

1、静态顺序表:使用定长数组存储元素

2、动态顺序表:使用动态开辟的数组存储

更推荐大家用动态内存开辟这种做法,因为更符合实际需求,那么我就来讲解一下顺序表的接口实现(动态顺序表)

1.2 接口实现

1.2.1 动态顺序表的初始化

cs 复制代码
void Inital(SeqList* psl)
{
	SLDataType* ptr = (SLDataType*)malloc(sizeof(SLDataType) * 3);//首先开辟3个变量的空间
	if (ptr == NULL)
	{
		perror("Inital malloc");
		return;
	}
	psl->arr = ptr;
	ptr = NULL;
	psl->capcity = 3;
	psl->sz = 0;
}

首先,先开辟三个变量的空间,用arr来管理,我们初始容量就置为3,有效数组的长度就是0

1.2.2 销毁顺序表

cs 复制代码
void Destory(SeqList* psl)
{
	free(psl->arr);
	psl->arr = NULL;
	psl->capcity = 0;
	psl->sz = 0;

}

1.2.3 顺序表的打印

cs 复制代码
void PrintSeqList(SeqList* psl)
{
	assert(psl);
	int i = 0;
	for (i = 0; i < psl->sz; i++)
	{
		printf("%d ", psl->arr[i]);
	}
	printf("\n");
}

就是我们是使用sz来控制顺序表的长度 ,打印顺序表的数据用sz控制即可、

1.2.4 顺序表动态开辟空间容量的检测

这个很重要,我们不敢没有开辟那块空间就去存储,这是很危险的,一定是及时扩容,那么这个检查内存空间是否充足的这个函数就很重要。

思想:其实很简单,只要sz和capcity相同,那么就扩容咯对不对,看代码

cs 复制代码
void CheckCapcity(SeqList* psl)
{
	assert(psl);

	if (psl->capcity == psl->sz)
	{
		SLDataType* ptr = (SLDataType*)realloc(psl->arr, sizeof(SLDataType) * psl->capcity * 2);
		if (ptr == NULL)
		{
			perror("CheckCapcity realloc");
			return;
		}
		psl->arr = ptr;
		psl->capcity *= 2;
		printf("增容成功\n");
	}
}

1.2.5 往顺序表中插入数据

思想:1、需要一个位置和一个数,这个位置之后的数从最后一位开始向右移动,

2、移动完成后,在该位置插入数据即可

cs 复制代码
void SeqListInsert(SeqList* psl, int pos, int x)
{
	assert(psl);
	assert(pos >= 0 && pos <= psl->sz);
	//确定是否需要增容
	CheckCapcity(psl);
	for (int i = psl->sz - 1; i >= pos; i--)
	{
		psl->arr[i + 1] = psl->arr[i];
	}
	psl->arr[pos] = x;
	psl->sz++;
}

延伸出两个极端情况,一个是尾部插入,一个是头插

(一)尾部插入

尾插实际上是在顺序表最末尾插入,其实就是在sz这个位置插入,那我们把pos改为psl->sz是不是就是可以了

cs 复制代码
void SeqListPushBack(SeqList* psl, int x)
{
	assert(psl);
	SeqListInsert(psl, psl->sz, x);
}

(二)头插

头插实际上是在顺序表开头插入,那么我们知道,实际上把pos改为0是不是就可以

cs 复制代码
void SeqListPushFront(SeqList* psl, int x)
{
	assert(psl);
	SeqListInsert(psl, 0, x);
}

1.2.6 判断数字是否在顺序表中存在

这个函数是用于判断函数是否在顺序表中存在,如果不存在返回-1,如果存在返回下标

cs 复制代码
int  SeqListFindNumber(SeqList* psl, int x)
{
	assert(psl);
	for (int i = 0; i < psl->sz; i++)
	{
		if (psl->arr[i] == x)
			return i;
	}
	return -1;
}

1.2.7 顺序表删除数据

有两种情况,一种是删除顺序表这个位置的数据,一种是删除顺序表这个数字(如果有重复只能删除一个,这就是顺序表不好的地方,链表直接指向下一个即可)

第一种情况:按照找数字的方式删除

cs 复制代码
void SeqListByEraseNumber(SeqList* psl, int x)
{
	assert(psl);
	if (psl->sz == 0)
	{
		printf("没有数据,不用删除\n");
		return;
	}
	int ret = SeqListFindNumber(psl, x);
	if (ret != -1)
	{
		for (int i = ret; i < psl->sz - 1; i++)
		{
			psl->arr[i] = psl->arr[i + 1];
		}
		psl->sz--;
		return;
	}
	printf("没有这个数,无法删除\n");
}

就是我找到这个数,返回这个数的下标,我需要从后往前覆盖掉这个数是不是就可以了。

第二种情况:按照找位置的方式删除

cs 复制代码
void SeqListByErasePosition(SeqList* psl, int pos)
{
	assert(psl);
	assert(pos >= 0 && pos < psl->sz);
	if (psl->sz == 0)
	{
		printf("没有数据,不用删除\n");
		return;
	}
	for (int i = pos; i < psl->sz - 1; i++)
	{
		psl->arr[i] = psl->arr[i + 1];
	}
	psl->sz--;
}

实际上和上面的思想一样,我们输入要删除的下标,还是从后往前覆盖就可以了

还有两个极端情况,一种是删除最后一个,叫尾删,一种是删除第一个,叫头删

(一)尾删

cs 复制代码
void SeqListPopBack(SeqList* psl)
{
	assert(psl);
	SeqListByPosition(psl, psl->sz - 1);

}

(二)头删

cs 复制代码
void SeqListPopFront(SeqList* psl)
{
	assert(psl);
	SeqListByPosition(psl, 0);
}

1.2.8 查找数字

思想:输入一个数,返回这个数的下标

cs 复制代码
void SeqListFind(SeqList* psl, int x)
{
	assert(psl);
	if (psl->sz == 0)
	{
		printf("没有数据,无法查找\n");
		return;
	}
	int ret = SeqListFindNumber(psl, x);
	if (ret != -1)
	{
		printf("找到了%d,下标是:%d\n", x, ret);
		return;
	}
	printf("找不到了");
}

1.2.9 修改顺序表中的数

还是两个情况,第一种情况,我想把某一个数改为某一个数,第二种情况,我想把某一个位置的数改为某一个数字。

(一)情况一

cs 复制代码
void SeqListModifyByNumber(SeqList* psl, int x, int y)
{
	assert(psl);
	if (psl->sz == 0)
	{
		printf("没有数据,无法修改\n");
		return;
	}
	int ret = SeqListFindNumber(psl, x);
	if (ret != -1)
	{
		psl->arr[ret] = y;
	}
}

(二)情况二

cs 复制代码
void SeqListModifyByPosition(SeqList* psl, int pos, int x)
{
	assert(psl);
	if (psl->sz == 0)
	{
		printf("没有数据,无法修改\n");
		return;
	}
	psl->arr[pos] = x;
}

二、完整的顺序表代码

1.test.c主函数代码

c 复制代码
#define  _CRT_SECURE_NO_WARNINGS 1


#include "seqlist.h"
void menu()
{
	printf("***************************************\n");
	printf("1、尾插数据  2、尾删数据\n");
	printf("3、头插数据  4、头删数据\n");
	printf("5、任意插入数据  6、任意删除数据\n");
	printf("7、打印数据  8、查找\n");
	printf("9、修改数据  0、退出\n");
	printf("***************************************\n");
}

int main()
{
	SeqList list;
	Inital(&list);
	//test1();
	//test2();
	//test3();
	//test4();
	//菜单
	int input = 0;
	int add = 0;
	int n = 0;
	int signel = 0;
	int Pos = 0;
	int prt = 0;
	do
	{
		menu();
		printf("请输入要进行的操作\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
		{
			printf("请输入希望尾插的个数\n");
			scanf("%d", &n);
			while (n--)
			{
				printf("请输入希望插入的数字\n");
				scanf("%d", &add);
				SeqListPushBack(&list, add);
			}
			break;
		}
		case 2:
		{
			printf("要尾删几个数据\n");
			scanf("%d", &n);
			while (n--)
			{
				SeqListPopBack(&list);
			}
			break;
		}
		case 3:
		{
			printf("请输入希望头插的个数\n");
			scanf("%d", &n);
			while (n--)
			{
				printf("请输入希望插入的数字\n");
				scanf("%d", &add);
				SeqListPushFront(&list, add);
			}
			break;
		}
		case 4:
		{
			printf("要头删几个数据\n");
			scanf("%d", &n);
			while (n--)
			{
				SeqListPopFront(&list);
			}
			break;
		}
		case 5:
		{
			printf("请输入要插入的个数:");
			scanf("%d", &n);
			while (n--)
			{
				printf("请输入要插入的位置\n");
				scanf("%d %d", &Pos, &add);
				SeqListInsert(&list, Pos, add);
			}
			break;
		}
		case 6:
		{
			printf("请输入删除的方式(位置\\数字(0\\1))");
			scanf("%d", &signel);
			if (signel == 0)
			{
				printf("请输入要删除的个数:");
				scanf("%d", &n);
				while (n--)
				{
					printf("请输入要删除的位置\n");
					scanf("%d", &Pos);
					SeqListByErasePosition(&list, Pos);
				}
			}
			else
			{
				printf("请输入要删除的个数:");
				scanf("%d", &n);
				while (n--)
				{
					printf("请输入要删除的数字\n");
					scanf("%d", &add);
					SeqListByEraseNumber(&list, add);
				}
			}
			break;
		}
		case 7:
			PrintSeqList(&list);
			break;
		case 8:
		{
			printf("请输入要查找的数字:");
			scanf("%d", &add);
			SeqListFind(&list,add);
			break;
		}
		case 9:
		{
			{
				printf("请输入修改的方式(位置\\数字(0\\1))");
				scanf("%d", &signel);
				if (signel == 0)
				{
					printf("请输入要修改的个数:");
					scanf("%d", &n);
					while (n--)
					{
						printf("请输入要修改的位置和要修改的值\n");
						scanf("%d %d", &Pos,&add);
						SeqListModifyByPosition(&list, Pos,add);
					}
				}
				else
				{
					printf("请输入要修改的个数:");
					scanf("%d", &n);
					while (n--)
					{
						printf("请输入要原顺序表的数字和要修改的数字\n");
						scanf("%d %d", &add,&prt);
						SeqListModifyByNumber(&list, add,prt);
					}
				}
				break;
			}
		}
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("输入非法,请重新输入\n");
			break;
		}
	} while (input);
	Destory(&list);
	return 0;
}

2.seqlist.h主函数声明代码

cs 复制代码
#pragma once


#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

#define N 7
typedef int SLDataType;

静态顺序表
//struct SeqList
//{
//	SLDataType arr[N];  //定长数组 
//	int sz;  //有效数据的长度
//};



//动态顺序表
typedef struct SeqList
{
	SLDataType* arr;  //动态开辟的内存数组
	int sz;  //有效数据的长度
	int capcity; //动态开辟可用的空间容量
}SeqList;


void Inital(SeqList* psl);

void Destory(SeqList* psl);

void SeqListInsert(SeqList* psl, int pos, int x);

void CheckCapcity(SeqList* psl);

void PrintSeqList(SeqList* psl);

void SeqListPushBack(SeqList* psl, int x);

void SeqListPushFront(SeqList* psl, int x);

void SeqListPopBack(SeqList* psl);
//版本1 找数字方法删除
void SeqListByEraseNumber(SeqList* psl, int x);

//版本2 找位置方式删除
void SeqListByErasePosition(SeqList* psl, int pos);
//尾部删除
void SeqListPopBack(SeqList* psl);
//头部删除
void SeqListPopFront(SeqList* psl);
int SeqListFindNumber(SeqList* psl, int x);

//顺序表查找
void SeqListFind(SeqList* psl, int x);
//顺序表修改
void SeqListModifyByNumber(SeqList* psl, int modify, int to);
//按位置修改
void SeqListModifyByPosition(SeqList* psl, int pos, int x);
//按数字修改
void SeqListModifyByNumber(SeqList* psl, int x, int y);

3. seqlist.c函数实现代码

cs 复制代码
#define  _CRT_SECURE_NO_WARNINGS 1


#include "seqlist.h"



void Inital(SeqList* psl)
{
	assert(psl);
	SLDataType* ptr = (SLDataType*)malloc(sizeof(SLDataType) * 3);//首先开辟3个变量的空间
	if (ptr == NULL)
	{
		perror("Inital malloc");
		return;
	}
	psl->arr = ptr;
	ptr = NULL;
	psl->capcity = 3;
	psl->sz = 0;
}

void Destory(SeqList* psl)
{
	free(psl->arr);
	psl->arr = NULL;
	psl->capcity = 0;
	psl->sz = 0;
}
//确定是否需要增容
void CheckCapcity(SeqList* psl)
{
	assert(psl);

	if (psl->capcity == psl->sz)
	{
		SLDataType* ptr = (SLDataType*)realloc(psl->arr, sizeof(SLDataType) * psl->capcity * 2);
		if (ptr == NULL)
		{
			perror("CheckCapcity realloc");
			return;
		}
		psl->arr = ptr;
		psl->capcity *= 2;
		printf("增容成功\n");
	}
}



void SeqListInsert(SeqList* psl, int pos, int x)
{
	assert(psl);
	assert(pos >= 0 && pos <= psl->sz);
	//确定是否需要增容
	CheckCapcity(psl);
	for (int i = psl->sz - 1; i >= pos; i--)
	{
		psl->arr[i + 1] = psl->arr[i];
	}
	psl->arr[pos] = x;
	psl->sz++;
}

//尾部插入
void SeqListPushBack(SeqList* psl, int x)
{
	assert(psl);
	SeqListInsert(psl, psl->sz, x);
}
//头部插入
void SeqListPushFront(SeqList* psl, int x)
{
	assert(psl);
	SeqListInsert(psl, 0, x);
}


void PrintSeqList(SeqList* psl)
{
	assert(psl);
	int i = 0;
	for (i = 0; i < psl->sz; i++)
	{
		printf("%d ", psl->arr[i]);
	}
	printf("\n");
}

int  SeqListFindNumber(SeqList* psl, int x)
{
	assert(psl);
	for (int i = 0; i < psl->sz; i++)
	{
		if (psl->arr[i] == x)
			return i;
	}
	return -1;
}
void SeqListByEraseNumber(SeqList* psl, int x)
{
	assert(psl);
	if (psl->sz == 0)
	{
		printf("没有数据,不用删除\n");
		return;
	}
	int ret = SeqListFindNumber(psl, x);
	if (ret != -1)
	{
		for (int i = ret; i < psl->sz - 1; i++)
		{
			psl->arr[i] = psl->arr[i + 1];
		}
		psl->sz--;
		return;
	}
	printf("没有这个数,无法删除\n");
}


void SeqListByErasePosition(SeqList* psl, int pos)
{
	assert(psl);
	assert(pos >= 0 && pos < psl->sz);
	if (psl->sz == 0)
	{
		printf("没有数据,不用删除\n");
		return;
	}
	for (int i = pos; i < psl->sz - 1; i++)
	{
		psl->arr[i] = psl->arr[i + 1];
	}
	psl->sz--;
}


void SeqListPopBack(SeqList* psl)
{
	assert(psl);
	SeqListByErasePosition(psl, psl->sz - 1);

}


void SeqListPopFront(SeqList* psl)
{
	assert(psl);
	SeqListByErasePosition(psl, 0);
}


void SeqListFind(SeqList* psl, int x)
{
	assert(psl);
	if (psl->sz == 0)
	{
		printf("没有数据,无法查找\n");
		return;
	}
	int ret = SeqListFindNumber(psl, x);
	if (ret != -1)
	{
		printf("找到了%d,下标是:%d\n", x, ret);
		return;
	}
	printf("找不到了\n");
}


void SeqListModifyByNumber(SeqList* psl, int x, int y)
{
	assert(psl);
	if (psl->sz == 0)
	{
		printf("没有数据,无法修改\n");
		return;
	}
	int ret = SeqListFindNumber(psl, x);
	if (ret != -1)
	{
		psl->arr[ret] = y;
	}
}

void SeqListModifyByPosition(SeqList* psl, int pos, int x)
{
	assert(psl);
	if (psl->sz == 0)
	{
		printf("没有数据,无法修改\n");
		return;
	}
	psl->arr[pos] = x;
}

总结

1、建议大家一定要动手实践一下这个顺序表,和我之前讲过的通讯录很像

2、一定要有耐心,数据结构这块知识不多但是不好理解

3、搞清楚顺序表的原理

如果这份博客对大家有帮助,希望各位给小马一个大大的点赞鼓励一下,如果喜欢,请收藏一下,谢谢大家!!!

制作不易,如果大家有什么疑问或给小马的意见,欢迎评论区留言。

相关推荐
浅念同学3 小时前
算法-常见数据结构设计
java·数据结构·算法
UndefindX3 小时前
PAT甲级1006 :Sign In and Sign Out
数据结构·算法
杨和段4 小时前
简介空间复杂度
数据结构
Overboom6 小时前
[数据结构] --- 线性数据结构(数组/链表/栈/队列)
数据结构
心死翼未伤7 小时前
【MySQL基础篇】多表查询
android·数据结构·数据库·mysql·算法
Beast Cheng7 小时前
07-7.1.1 查找的基本概念
数据结构·笔记·考研·算法·学习方法
狂放不羁霸8 小时前
保研复习 | 数据结构
数据结构
hummhumm9 小时前
数据结构第3节: 抽象数据类型
数据结构·spring boot·spring·spring cloud·java-ee·maven·intellij-idea
Overboom9 小时前
[数据结构] --- 树
数据结构
我们的五年9 小时前
【算法:贪心】:贪心算法介绍+基础题(四个步骤);柠檬水找零(交换论证法)
c语言·数据结构·c++·算法·贪心算法