数据结构——顺序表

目录

引言

什么是顺序表

顺序表的功能

顺序表的定义

功能实现

1.顺序表的初始化

(1)代码实现

(2)复杂度分析

2.打印数据

(1)代码实现

(2)复杂度分析

3.尾插数据

(1)检查空间

(2)插入数据

(3)复杂度分析

4.尾删数据

(1)代码实现

(2)复杂度分析

5.头插数据

(1)代码实现

(2)复杂度分析

6.头删数据

(1)代码实现

(2)复杂度分析

7.数据查找

(1)代码实现

(2)复杂度分析

8.指定位置数据修改

(1)代码实现

(2)复杂度分析

9.指定位置数据删除

(1)代码实现

(2)复杂度分析

10.指定位置数据插入

(1)代码实现

(2)复杂度分析

11.销毁顺序表

(1)代码实现

(2)复杂度分析

完整代码

1.SeqList.h

2.SeqList.c

结束语


引言

在 数据结构——基础 中我们学习了数据结构的一些基础知识,今天我们接着来学习一种数据结构——顺序表

什么是顺序表

顺序表是线性表的一种顺序存储结构,即用一段地址连续的存储单元依次存储线性表的数据元素。

今天我们要学习的是动态顺序表。

它与数组非常类似,但是相比于静态顺序表有一个非常明显的优点——可以动态内存增长空间大小。

顺序表的功能

顺序表可以大致包含如下几个功能:

1.初始化顺序表中的数据。

2.打印顺序表中的数据。

3.对顺序表进行尾插(末尾插入数据)。

4.对顺序表进行尾删(末尾删除数据)。

5.对顺序表进行头插(开头插入数据)。

6.对顺序表进行头删(开头删除数据)。

7.对顺序表数据进行查找。

8.对顺序表数据进行修改。

9.对指定位置的数据删除。

10.对指定位置的数据插入。

11.销毁顺序表。

顺序表的定义

定义动态顺序表我们首先需要一个动态内存开辟的空间,当前数据的个数(size),以及方便扩容的容量大小(capacity)

//定义顺序表的结构

//define N 100
//静态顺序表
//struct SeqList
//{
//	int arr[N]; //空间固定
//	int size;	//有效数据个数
//}

typedef int SLDataType;
//动态顺序表
typedef struct SeqList
{
	SLDataType* arr;
	int size;		//有效数据个数
	int capacity;	//空间大小
}SL;

功能实现

1.顺序表的初始化

(1)代码实现

顺序表的初始化我们可以把空间和有效数据个数都设置为0。

void SLInit(SL* ps)
{
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

(2)复杂度分析

时间复杂度:由于所有这些操作都是常数时间操作(即它们的执行时间不依赖于输入的大小),因此该函数的时间复杂度是 O(1)。

空间复杂度:由于该函数没有使用任何额外的空间,因此空间复杂度为O(1)

2.打印数据

(1)代码实现

//顺序表的打印
void SLPrint(SL* ps)
{
	assert(ps);
	if (ps->size == 0)
	{
		printf("该顺序表为空\n");
		return;
	}
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");
}

(2)复杂度分析

时间复杂度:由于打印了顺序表中的所有数据,因此时间复杂度为O(N)。

空间复杂度:由于打印顺序表并不需要开辟格外的空间,因此空间复杂度为O(1)。

3.尾插数据

尾插数据就是在顺序表的末尾插入数据,在插入数据之前需要先检查顺序表的空间够不够,不够的话我们需要进行扩容处理。

(1)检查空间

void SLCheckCapacity(SL* ps)
{
	//插入数据之前先看空间够不够
	if (ps->capacity == ps->size)
	{
		//申请空间
		//malloc calloc realloc int arr[100]--->增容realloc
		//三目表达式
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		//创建一个变量,避免申请失败
		SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));
		//判断是否成功
		if (tmp == NULL)
		{
			perror("realloc fail:");
			exit(1);	//直接退出程序,不再执行
		}
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
}

(2)插入数据

//尾插
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);    //检查空间是否足够

    //ps->arr[ps->size] = x;
    //++ps->size;
	ps->arr[ps->size++] = x;    //尾插数据
}

(3)复杂度分析

时间复杂度:时间复杂度为O(1)。

空间复杂度:最坏的情况是会进行扩容处理,空间复杂度为O(n)。

4.尾删数据

尾删数据十分容易,只需要size--就可以了。但是,要注意:一定要确保顺序表中有数据。

(1)代码实现

void SLPopBack(SL* ps)
{
	assert(ps);
	//判断顺序表是否为空
	assert(ps->size);
	ps->size--;
}

(2)复杂度分析

时间复杂度:没有变量影响时间复杂度,因此时间复杂度为O(1)。

空间复杂度:没有变量影响空间复杂度,因此空间复杂度为O(1)。

5.头插数据

头插数据是指在顺序表的起始位置插入数据。

(1)代码实现

//头插
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);
	//让顺序表中已经存在的数据整体往后挪一位
	for (int i = ps->size; i > 0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];	//即arr[1]=arr[0]以此类推
	}
	ps->arr[0] = x;
	ps->size++;
}

(2)复杂度分析

时间复杂度:因为从头部插入数据,后续数据需要依次覆盖,因此时间复杂度为O(N)。

空间复杂度:因为可能会进行扩容,按最坏的情况来算,因此空间复杂度为O(N)。

6.头删数据

删除头部数据,需要依次往前覆盖。

(1)代码实现

// 头删
void SLPopFront(SL* ps)
{
	assert(ps);
	//判断顺序表是否为空
	assert(ps->size);
	//数据整体往前移动一位
	for (int i = 0; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

(2)复杂度分析

时间复杂度:因为从头部删除数据,后续数据需要依次往前覆盖,因此时间复杂度为O(N)。

空间复杂度:没有开辟新的空间,因此空间复杂度为O(1)。

7.数据查找

输入参数,在顺序表找到指定的值并返回下标,找不到则返回-1。

(1)代码实现

//顺序表的查找
int SLFind(SL* ps, SLDataType x)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		//找到
		if (ps->arr[i] == x)
		{
			return i;
		}
	}
	//没找到
	return -1;
}

(2)复杂度分析

时间复杂度:根据最坏的情况,即遍历了整个顺序表,时间复杂度为O(n)。

空间复杂度:没有额外的空间消耗,因此空间复杂度为O(1)。

8.指定位置数据修改

(1)代码实现

//指定位置数据修改
void SLModify(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(ps->size);
	assert(pos>=0 && pos < ps->size);
	ps->arr[pos] = x;
}

(2)复杂度分析

时间复杂度:因为是对指定的下标数据进行修改,因此时间复杂度为O(1)。

空间复杂度:没有开辟额外的空间,因此空间复杂度为O(1)。

9.指定位置数据删除

(1)代码实现

//指定位置删除数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;  //有效数据-1
}

(2)复杂度分析

时间复杂度:由于需要将指定位置之后的数据往前覆盖,因此时间复杂度是O(n)。

空间复杂度:由于没有额外的空间消耗,因此空间复杂度为O(1)。

10.指定位置数据插入

(1)代码实现

//在指定位置之前插入数据
//pos为指定位置
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	//先检查空间够不够
	SLCheckCapacity(ps);
	//让pos及之后的数据整体向后移动一位
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[pos] = x;
	ps->size++;
}

(2)复杂度分析

时间复杂度:由于指定位置之后的数据需要往后覆盖,因此时间复杂度为O(n)。

空间复杂度:由于可能需要扩容,因此空间复杂度为O(N)。

11.销毁顺序表

(1)代码实现

//顺序表的销毁
void SLDestroy(SL* ps)
{
	if (ps->arr)
	{
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

(2)复杂度分析

时间复杂度:由于没有额外的时间消耗,因此时间复杂度为O(1)。

空间复杂度:由于没有额外的空间消耗,因此空间复杂度为O(1)。

完整代码

1.SeqList.h

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

//定义顺序表的结构

//define N 100
//静态顺序表
//struct SeqList
//{
//	int arr[N];
//	int size;	//有效数据个数
//}

typedef int SLDataType;
//动态顺序表
typedef struct SeqList
{
	SLDataType* arr;
	int size;		//有效数据个数
	int capacity;	//空间大小
}SL;

//顺序表初始化
void SLInit(SL* ps);
//顺序表的销毁
void SLDestroy(SL* PS);
//顺序表的打印
void SLPrint(SL s);


//尾部插入
void SLPushBack(SL* ps, SLDataType x);
//头部插入
void SLPushFront(SL* ps, SLDataType x);

//尾部删除
void SLPopBack(SL* ps);
//头部删除
void SLPopFront(SL* ps);

//指定位置之前插入
void SLInsert(SL* ps, int pos, SLDataType x);
//指定位置删除数据
void SLErase(SL* ps, int pos);
//查找数据
int SLFind(SL* ps, SLDataType x);

2.SeqList.c

#include"SeqList.h"

void SLInit(SL* ps)
{
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

//顺序表的销毁
void SLDestroy(SL* ps)
{
	if (ps->arr)
	{
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

void SLCheckCapacity(SL* ps)
{
	//插入数据之前先看空间够不够
	if (ps->capacity == ps->size)
	{
		//申请空间
		//malloc calloc realloc int arr[100]--->增容realloc
		//三目表达式
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		//创建一个变量,避免申请失败
		SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));
		//判断是否成功
		if (tmp == NULL)
		{
			perror("realloc fail:");
			exit(1);	//直接退出程序,不再执行
		}
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
}

//尾插
void SLPushBack(SL* ps, SLDataType x)
{
	/*if (ps == NULL)
	{
		return;
	}*/
	assert(ps);

	/*ps->arr[ps->size] = x;
	++ps->size;*/
	SLCheckCapacity(ps);
	ps->arr[ps->size++] = x;
}

//头插
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);
	//让顺序表中已经存在的数据整体往后挪一位
	for (int i = ps->size; i > 0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];	//即arr[1]=arr[0]以此类推
	}
	ps->arr[0] = x;
	ps->size++;
}

//顺序表的打印
void SLPrint(SL* ps)
{
	assert(ps);
	if (ps->size == 0)
	{
		printf("该顺序表为空\n");
		return;
	}
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");
}

void SLPopBack(SL* ps)
{
	assert(ps);
	//判断顺序表是否为空
	assert(ps->size);
	ps->size--;
}

// 头删
void SLPopFront(SL* ps)
{
	assert(ps);
	//判断顺序表是否为空
	assert(ps->size);
	//数据整体往前移动一位
	for (int i = 0; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

//在指定位置之前插入数据
//pos为指定位置
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	//先检查空间够不够
	SLCheckCapacity(ps);
	//让pos及之后的数据整体向后移动一位
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[pos] = x;
	ps->size++;
}

//指定位置删除数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;  
}

//顺序表的查找
int SLFind(SL* ps, SLDataType x)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		//找到
		if (ps->arr[i] == x)
		{
			return i;
		}
	}
	//没找到
	return -1;
}

//指定位置数据修改
void SLModify(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(ps->size);
	assert(pos>=0 && pos < ps->size);
	ps->arr[pos] = x;
}

结束语

求点赞收藏关注!!!

十分感谢!!!

  • 15
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值