【数据结构】——顺序表(增删查改)

目录

前言:

顺序表:

1、概念及分类

1.1顺序表分类

静态顺序表

动态顺序表

2、接口实现

2.1功能要求

2.2功能实现

💡初始化顺序表

💡销毁顺序表

💡顺序表尾插入

💡检查是否扩容

💡打印顺序表

💡顺序表头插入

💡顺序表头删除

💡顺序表尾删除

💡顺序表pos位置插入值

💡顺序表删除pos的位置

总代码

test.c

SeqList.c

SeqList.h


前言:

数据结构是计算机存储、组织数据的方式。

数据结构是指相互之间存在⼀种或多种特定关系的数据元素的集合。数据结构反映数据的内部构成,即数据由那部分构成,以什么方式构成,以及数据元素之间呈现的结构。

能够存储数据(如顺序表、链表等结构)
存储的数据能够方便查找

顺序表:

数据结构分为:线性表非线性表

顺序表就是线性表中的一个小类。

何为线性表:线性表是指n个具有相同性质的数据元素的有限序列,

常见的线性表有:顺序表、链表、栈、队列、字符串等等。

**注:**线性表的物理结构不一定是线性的,它在逻辑结构上一定是线性的(这个很好理解,等我们学完顺序表和单链表这对黄金搭档,就明白这句话的含义了。(逻辑结构类似于想象的,比如箭头这种东西在内存中是存在的))
顺序表是线性表,顺序表在逻辑结构和物理结构上都是线性的

1、概念及分类

顺序表(SeqList):顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构(连续存储数据,不能跳跃)。

类似于数组,但是顺序表是连续的(只能从头开始连续),数组不需要连续;

1.1顺序表分类

静态顺序表

概念:使用定长数组存储元素

缺陷:空间给少了不够⽤,给多了造成空间浪费

//静态顺序表
typedef int SLDataType;
#define N 7
struct SeqList
{
    SLDataType arr[N];  //定长数组
    int size;       //有效数据个数
}SL;
动态顺序表

概念:使用动态开辟的数组存储。

//动态顺序表
typedef int SLDateType;
typedef struct SeqList
{
    SLDateType* array;  //指向动态开辟的数组
    size_t size;  //数据中存储的数据
    size_t capacity;   //数组的容量
}SeqList;

建议用动态顺序表,比起静态顺序表,动态的更加好调整顺序表的大小。接下来,我也会以动态顺序表为例,介绍如何实现动态顺序表的增删查改。

2、接口实现

2.1功能要求

#pragma once
#include<stdio.h>
#include<assert.h>

//对顺序表的初始化
void SeqListInit(SL* ps);

//对顺序表的销毁
void SeqListDestroy(SL* ps);

//对顺序表的打印
void SeqListPrint(SL* ps);

//对顺序表的尾插入
void SeqListPushBack(SL* ps, SLDataType x);

//对顺序表的头插入
void SeqListPushFront(SL* ps, SLDataType x);

//对顺序表头删除
void SeqListPopFront(SL* ps);

//对顺序表的尾删除
void SeqListPopBack(SL* ps);

// 顺序表查找
//int SeqListFind(SeqList* ps, SLDateType x);
 
// 顺序表在pos位置插入x
void SeqListInsert(SL* ps, int pos, SLDataType x);


// 顺序表删除pos位置的值
void SeqListErase(SL* ps, int pos);

2.2功能实现

💡初始化顺序表

没啥好说的,在【C语言】系列都讲过了,3 2 1 上链接

//初始化
void SeqListInit(SL* ps)
{
	assert(ps);
	ps->arry = NULL;
	ps->size = 0;
	ps->capacity = 0;
}
💡销毁顺序表
//销毁
void SeqListDestroy(SL* ps)
{
	assert(ps);
	if (ps->arry != NULL)
	{
		free(ps->arry);
		ps->arry = NULL;
		ps->size = 0;
		ps->capacity = 0;
	}
}
💡顺序表尾插入

这里需要检查是否需要扩容,我们还需要提前封装一个函数

//对顺序表的尾插入
void SeqListPushBack(SL* ps, SLDataType x)
{
	assert(ps);
	//检查是否需要扩容
	CheckCapacity(ps);
	ps->arry[ps->size] = x;
	ps->size++;
}
💡检查是否扩容

在这里需要注意的就是,我们初始化的时候,capacity是0,如果用常规*2方式扩容,0*2还是0;

所以这里可以用上一个三目操作符来避免;realloc可以对空指针进行开辟空间,相当于malloc

//检查是否需要扩容
void CheckCapacity(SL* ps)
{
	assert(ps);
	if (ps->size == ps->capacity)	//需要扩容
	{
		int new =ps->capacity == 0 ? 4 :ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->arry, sizeof(SLDataType)*new);
		//检查是否扩容成功
		if (tmp == NULL)
		{
			perror("realloc");
			return;
		}
		ps->arry = tmp;
		ps->capacity = new;
		printf("扩容成功\n");
	}
}
💡打印顺序表
//打印顺序表
void SeqListPrint(SL* ps)
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->arry[i]);
	}
	printf("\n");
}
💡顺序表头插入

不管是头插入还是尾插入以及后面的任意位置插入,都需要检查是否需要扩容,

头插入就相当于先将数据往右边移动,头位置空出来,然后将新数据插入即可

//头插入
void SeqListPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	CheckCapacity(ps);
	int begin = ps->size;
	while (begin>0)
	{
		ps->arry[begin] = ps->arry[begin - 1];
		begin--;
	}
	ps->arry[0] = x;
	ps->size++;
}
💡顺序表头删除

因为顺序表的逻辑结构和物理结构一致,数据前后紧密相连的,所以可以直接将数据往前覆盖

//头删除
void SeqListPopFront(SL* ps)
{
	assert(ps);
	int begin = 1;
	while (begin > 0 &&  begin < ps->size )
	{
		ps->arry[begin - 1] = ps->arry[begin];
		begin++;
	}
	ps->size--;
}
💡顺序表尾删除

需要注意的就说size跑到负数去,我们采取"七匹狼式警告",直接"竹条炒肉"

//尾删除
void SeqListPopBack(SL* ps)
{
	assert(ps);
	//对size检查 防止越界
	assert(ps->size > 0);
	ps->size--;
}
💡顺序表pos位置插入值

在容量内任意位置插入,将该位置后的数据往后移,将新元素赋值

// 顺序表在pos位置插入x
void SeqListInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	CheckCapacity(ps);
	int begin = ps->size;
	while (begin > pos)
	{
		ps->arry[begin] = ps->arry[begin - 1];
		begin--;
	}
	ps->arry[pos] = x;
	ps->size++;
}
💡顺序表删除pos的位置
// 顺序表删除pos位置的值
void SeqListErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	int begin = pos;
	while (begin < ps->size)
	{
		ps->arry[begin] = ps->arry[begin + 1];
		begin++;
	}
	ps->size--;
}

总代码

注意,我们完成每个功能实现,最好都单独测试一下,不要留到最后,不然就会这样

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
//11-4ݽṹ
#include<stdio.h>

void test1()
{
	SL s1;
	SeqListInit(&s1);
	SeqListPushBack(&s1,1);
	SeqListPrint(&s1);

	SeqListDestroy(&s1);
}

void test2()
{
	SL s2;
	SeqListInit(&s2);
	SeqListPushBack(&s2, 1);
	SeqListPushBack(&s2, 2);
	SeqListPushBack(&s2, 3);
	SeqListPushBack(&s2, 4);
	SeqListPushBack(&s2, 5);

	SeqListPushFront(&s2,10);
	SeqListPrint(&s2);
	
}

//void test3()
//{
//	SL s3;
//	SeqListInit(&s3);
//	SeqListPushBack(&s3, 1);
//	SeqListPushBack(&s3, 2);
//	SeqListPushBack(&s3, 3);
//	SeqListPushBack(&s3, 4);
//	SeqListPushBack(&s3, 5);
//	SeqListPrint(&s3);
//
//	SeqListPopFront(&s3);
//	SeqListPrint(&s3);
//}

void test4()
{
	SL s4;
	SeqListInit(&s4);
	SeqListPushBack(&s4, 1);
	SeqListPushBack(&s4, 2);
	SeqListPushBack(&s4, 3);
	SeqListPushBack(&s4, 4);
	SeqListPushBack(&s4, 5);
	SeqListPrint(&s4);

	SeqListInsert(&s4, 2, 66);
	SeqListPrint(&s4);
}
void test5()
{
	SL s5;
	SeqListInit(&s5);
	SeqListPushBack(&s5, 1);
	SeqListPushBack(&s5, 2);
	SeqListPushBack(&s5, 3);
	SeqListPushBack(&s5, 4);
	SeqListPushBack(&s5, 5);
	SeqListPrint(&s5);
	SeqListErase(&s5, 2);
	SeqListPrint(&s5);
}
int main()
{ 
	//test1();
	//test2();
	//test3();
	//test4();
	test5();
	return 0;
}

SeqList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
//初始化
void SeqListInit(SL* ps)
{
	assert(ps);
	ps->arry = NULL;
	ps->size = 0;
	ps->capacity = 0;
}
//销毁
void SeqListDestroy(SL* ps)
{
	assert(ps);
	if (ps->arry != NULL)
	{
		free(ps->arry);
		ps->arry = NULL;
		ps->size = 0;
		ps->capacity = 0;
	}
}
//检查是否需要扩容
void CheckCapacity(SL* ps)
{
	assert(ps);
	if (ps->size == ps->capacity)	//需要扩容
	{
		int new =ps->capacity == 0 ? 4 :ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->arry, sizeof(SLDataType)*new);
		//检查是否扩容成功
		if (tmp == NULL)
		{
			perror("realloc");
			return;
		}
		ps->arry = tmp;
		ps->capacity = new;
		printf("扩容成功\n");
	}
	
}
//对顺序表的尾插入
void SeqListPushBack(SL* ps, SLDataType x)
{
	assert(ps);
	//检查是否需要扩容
	CheckCapacity(ps);
	ps->arry[ps->size] = x;
	ps->size++;
}
//打印顺序表
void SeqListPrint(SL* ps)
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->arry[i]);
	}
	printf("\n");
}
//头插入
void SeqListPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	CheckCapacity(ps);
	int begin = ps->size;
	while (begin>0)
	{
		ps->arry[begin] = ps->arry[begin - 1];
		begin--;
	}
	ps->arry[0] = x;
	ps->size++;
}
//头删除
void SeqListPopFront(SL* ps)
{
	assert(ps);
	int begin = 1;
	while (begin > 0 &&  begin < ps->size )
	{
		ps->arry[begin - 1] = ps->arry[begin];
		begin++;
	}
	ps->size--;
}
//尾删除
void SeqListPopBack(SL* ps)
{
	assert(ps);
	//对size检查 防止越界
	assert(ps->size > 0);
	ps->size--;
}

// 顺序表在pos位置插入x
void SeqListInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	CheckCapacity(ps);
	int begin = ps->size;
	while (begin > pos)
	{
		ps->arry[begin] = ps->arry[begin - 1];
		begin--;
	}
	ps->arry[pos] = x;
	ps->size++;
}

// 顺序表删除pos位置的值
void SeqListErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	int begin = pos;
	while (begin < ps->size)
	{
		ps->arry[begin] = ps->arry[begin + 1];
		begin++;
	}
	ps->size--;

SeqList.h

#pragma once
#include<stdio.h>
#include<assert.h>
typedef int SLDataType;

typedef struct SeqList
{
	SLDataType* arry;	//动态开辟的数组
	int size;	//记录个数
	int capacity;	//记录容量
}SL;



//对顺序表的初始化
void SeqListInit(SL* ps);

//对顺序表的销毁
void SeqListDestroy(SL* ps);

//对顺序表的打印
void SeqListPrint(SL* ps);

//对顺序表的尾插入
void SeqListPushBack(SL* ps, SLDataType x);

//对顺序表的头插入
void SeqListPushFront(SL* ps, SLDataType x);

//对顺序表头删除
void SeqListPopFront(SL* ps);

//对顺序表的尾删除
void SeqListPopBack(SL* ps);

// 顺序表查找
//int SeqListFind(SeqList* ps, SLDateType x);
 
// 顺序表在pos位置插入x
void SeqListInsert(SL* ps, int pos, SLDataType x);


// 顺序表删除pos位置的值
void SeqListErase(SL* ps, int pos);
相关推荐
残月只会敲键盘4 分钟前
面相小白的php反序列化漏洞原理剖析
开发语言·php
ac-er88886 分钟前
PHP弱类型安全问题
开发语言·安全·php
ac-er88887 分钟前
PHP网络爬虫常见的反爬策略
开发语言·爬虫·php
爱吃喵的鲤鱼17 分钟前
linux进程的状态之环境变量
linux·运维·服务器·开发语言·c++
懒惰才能让科技进步39 分钟前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
DARLING Zero two♡43 分钟前
关于我、重生到500年前凭借C语言改变世界科技vlog.16——万字详解指针概念及技巧
c语言·开发语言·科技
Gu Gu Study1 小时前
【用Java学习数据结构系列】泛型上界与通配符上界
java·开发语言
Ni-Guvara1 小时前
函数对象笔记
c++·算法
芊寻(嵌入式)1 小时前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习
一颗松鼠1 小时前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript