数据结构之顺序表

目录

引言 

最基础的数据结构:数组 

顺序表与数组的区别 

静态顺序表

动态顺序表 

定义

初始化 

销毁 

尾插 

打印 

头插 

尾删 

头删 

指定插入 

指定删除 

查找 

修改 

源代码 

seqlist.h

seqlist.c


引言 

数据结构之路起点——顺序表(Sequential List)

最基础的数据结构:数组 

【思考】有了数组,为什么还要学习其他的数据结构 

假定数组有10个空间,已经使⽤了5个,向数组中插⼊数据步骤:
求数组的长度,求数组的 有效数据个数 ,向下标为数据有效个数的位置插⼊数据(注意:这⾥是否要判断数组是否满了,满了还能继续插⼊吗).....
假设数据量非常庞大,频繁的获取数组有效数据个数会影响程序执行效率。
结论 :最 基础 的数据结构能够提供的操作已经不能完全满⾜ 复杂 算法实现。

顺序表与数组的区别 

顺序表的 底层结构 数组 ,对数组的封装,实现了常用的 增删改查 接口

静态顺序表

这里使用typedef定义数据类型,使得后续方便改动 ;同时缩减结构体名称,方便书写

数据结构之顺序表_第1张图片

数据结构之顺序表_第2张图片

静态顺序表缺陷:空间给少了不够⽤,给多了造成空间浪费 

动态顺序表 

定义

数据结构之顺序表_第3张图片

数据结构之顺序表_第4张图片

 顺序表的各种功能,都是通过函数来实现的

初始化 

这里注意,函数参数是传结构体指针,这样才能在函数内部对顺序表进行各种解引用操作 

函数定义

对于动态顺序表,初始化则用malloc函数动态开辟内存空间 ;存储个数为0,容量初始置为4

数据结构之顺序表_第5张图片

销毁 

函数定义

销毁的实现就比较容易了,对于用free函数将动态开辟的空间进行释放,并将指针置为NULL ;再将存储个数和容量置为0

数据结构之顺序表_第6张图片

尾插 

在顺序表尾部插入数据 

函数定义 

根据当前已有的元素个数,对数组进行下标访问并赋值,size自增

普通写法

数据结构之顺序表_第7张图片

合并写法 

数据结构之顺序表_第8张图片

但是,我们会发现一个问题,如果尾插时,数组满了怎么办?那么,我们就需要扩容,定义一个函数专门来检测容量,如果容量满了,则进行扩容

数据结构之顺序表_第9张图片

我们使用realloc函数动态开辟空间进行扩容,而扩容的大小则为原来容量的2倍 (这样比较合理,扩容多了浪费空间,扩容少了空间又不够)

数据结构之顺序表_第10张图片

打印 

对顺序表进行了操作,总得打印出来看看吧~ 

函数定义

for循环遍历数组,打印对应下标的元素

数据结构之顺序表_第11张图片

运行结果

数据结构之顺序表_第12张图片

头插 

函数定义

与尾插相同,先检测容量,再用循环将数组中每个元素向后挪动一格,最后在头部插入数据,size自增

数据结构之顺序表_第13张图片

尾删 

函数定义

尾部删除的实现,就直接让size自减,使得不再能够访问尾部数据。尾删时要注意使用断言assert,保证size大于零,不会造成越界访问

数据结构之顺序表_第14张图片

头删 

函数定义

与尾删相同,使用断言assert,再用循环将数组中每个元素向前挪动一格,覆盖头部数据,实现头部删除,size自减

数据结构之顺序表_第15张图片

提醒:对于各种判断条件不清楚,请一定要画图!!! 

指定插入 

函数定义

输入指定位置的下标,用循环pos往后的所有数据挪动一格 ,再于指定位置插入数据,size自增;同样,断言assert保证pos不会越界

数据结构之顺序表_第16张图片

有些人有可能会疑惑,为什么不判断是否为头插和尾插呢?答案是,因为我们可以运用这个应用更广的指定插入,来实现头插和尾插 ,以此增强函数的复用性

尾插

数据结构之顺序表_第17张图片

头插

数据结构之顺序表_第18张图片

指定删除 

函数定义

 输入指定位置的下标,用循环pos往后的所有数据挪动一格 ,以此覆盖pos位置的数据,达到指定删除的目的;同样,断言assert保证pos不会越界(此处与指定插入比少了一个等号,仔细思考一下为什么?)

数据结构之顺序表_第19张图片

 与指定插入相同,延续函数的复用性,实现头删和尾删

头删

数据结构之顺序表_第20张图片

尾删

数据结构之顺序表_第21张图片

查找 

函数定义

for循环遍历数组,找到返回下标,找不到返回-1 

数据结构之顺序表_第22张图片

修改 

函数定义

同样,只要有pos就用断言assert,再直接通过下标访问数组进行修改 

数据结构之顺序表_第23张图片

在后续调试过程中发现,对于psl指针,如果有人误传了NULL,则会导致程序崩溃,所以最好在每个函数前断言assert,保证psl指针的有效性 

这样我们就完成了顺序表增删查改等功能的实现

源代码 

seqlist.h

#pragma once
#include
#include
#include

//静态顺序表
//#define N 10
//typedef int SLDataType;
//
//typedef struct SeqList
//{
//	SLDataType a[N];
//	int size;
//}SL;


//动态顺序表
typedef int SLDataType;

typedef struct SeqList
{
	SLDataType* a;
	int size;//存储的有效数据的个数
	int capacity;//容量
}SL;

//初始化
void SLInit(SL* psl);
//销毁
void SLDestroy(SL* psl);
//打印
void SLPrint(SL* psl);


//尾插
void SLPushBack(SL* psl, SLDataType x);
//头插
void SLPushFront(SL* psl, SLDataType x);
//尾删
void SLPopBack(SL* psl);
//头删
void SLPopFront(SL* psl);

//指定插入
void SLInsert(SL* psl, int pos, SLDataType x);
//指定删除
void SLErase(SL* psl, int pos);

//查找
int SLFind(SL* psl, SLDataType x);
//修改
void SLModify(SL* psl, int pos, SLDataType x);

seqlist.c
 

#define _CRT_SECURE_NO_WARNINGS 1
#include"seqlist.h"

void SLInit(SL* psl)
{
	assert(psl);
	psl->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);
	if (psl->a == NULL)
	{
		perror("malloc fail");
		return;
	}
	psl->size = 0;
	psl->capacity = 4;
}

void SLDestroy(SL* psl)
{
	assert(psl);
	free(psl->a);
	psl->a = NULL;
	psl->size = 0;
	psl->capacity = 0;
}

void SLPrint(SL* psl)
{
	assert(psl);
	int i = 0;
	for (i = 0; i < psl->size; i++)
	{
		printf("%d ", psl->a[i]);
	}
}

static void CheckCapacity(SL* psl)
{
	assert(psl);
	if (psl->size == psl->capacity)
	{
		SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType) * psl->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		psl->a = tmp;
		psl->capacity *= 2;
	}
}

void SLPushBack(SL* psl, SLDataType x)
{
	assert(psl);
	//CheckCapacity(psl);
	//psl->a[psl->size++] = x;
	SLInsert(psl, psl->size, x);
}

void SLPushFront(SL* psl, SLDataType x)
{
	assert(psl);
	//CheckCapacity(psl);
	//int end = psl->size - 1;
	//while (end >= 0)
	//{
	//	psl->a[end + 1] = psl->a[end];
	//	end--;
	//}
	//psl->a[0] = x;
	//psl->size++;
	SLInsert(psl, 0, x);
}

void SLPopBack(SL* psl)
{
	assert(psl);
	//assert(psl->size > 0);
	//psl->size--;
	SLErase(psl, psl->size - 1);
}

void SLPopFront(SL* psl)
{
	assert(psl);
	//assert(psl->size > 0);
	//int start = 0;
	//while (start < psl->size - 1)
	//{
	//	psl->a[start] = psl->a[start + 1];
	//	start++;
	//}
	//psl->size--;
	SLErase(psl, 0);
}

void SLInsert(SL* psl, int pos, SLDataType x)
{
	assert(psl);
	assert(pos >= 0 && pos <= psl->size);
	CheckCapacity(psl);
	int end = psl->size - 1;
	while (end >= pos)
	{
		psl->a[end + 1] = psl->a[end];
		end--;
	}
	psl->a[pos] = x;
	psl->size++;
}

void SLErase(SL* psl, int pos)
{
	assert(psl);
	assert(pos >= 0 && pos < psl->size);
	int start = pos + 1;
	while (start < psl->size)
	{
		psl->a[start - 1] = psl->a[start];
		start++;
	}
	psl->size--;
}

int SLFind(SL* psl, SLDataType x)
{
	assert(psl);
	int i = 0;
	for (i = 0; i < psl->size; i++)
	{
		if (psl->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}

void SLModify(SL* psl, int pos, SLDataType x)
{
	assert(psl);

	assert(pos >= 0 && pos < psl->size);
	psl->a[pos] = x;
}

你可能感兴趣的:(数据结构,数据结构,c语言)