「探索数据结构:了解顺序表」

🌠先赞后看,不足指正!🌠

🎈这将对我有很大的帮助!🎈

📝所属专栏:数据结构与算法

📝阿哇旭的主页:Awas-Home page

目录

 引言

1. 什么是顺序表?

2.  顺序表的分类

 2.1 顺序表和数组的区别

 2.2 静态顺序表

 2.3 动态顺序表 

3. 顺序表的功能

4. 顺序表的定义

 5. 具体功能实现  

 5.1 初始化顺序表

5.2 打印顺序表数据

5.3 对顺序表插入/删除数据

5.3.1 检查剩余空间大小

5.3.2 尾部插入数据 

5.3.3 尾部删除数据

5.3.4 头部插入数据

5.3.5 头部删除数据

5.4 指定位置插入/删除数据 

5.4.1 指定位置插入数据

5.4.2 指定位置删除数据

5.5 指定位置修改数据

5.6 查找顺序表数据 

5.7 销毁顺序表 

6. 完整代码 

6.1 Seqlist.h

6.2 Seqlist.c 

7. 结语


 

 引言

        今天,我们将接触一个全新的概念“数据结构”。顾名思义,数据结构是由“数据”和“结构”两词组合而来,接下来,我将为大家带来数据结构的初阶知识。本篇文章的主题是顺序表

          那么,话不多说,我们一起来看看吧!


1. 什么是顺序表?

        顺序表是一种线性表的存储结构,它是由一组物理地址连续的存储单元(通常是数组)依次存储线性表中的元素。顺序表中的元素在内存中是连续存储的,通过元素在数组中的下标来访问和操作元素。

        顺序表的优点是访问元素的时间复杂度为O(1),即可以通过下标直接访问元素,操作效率高;缺点是插入和删除元素时需要移动其他元素,时间复杂度为O(n),效率较低。


2.  顺序表的分类

 2.1 顺序表和数组的区别

        顺序表不同于数组,顺序表的底层结构是数组,对数组的封装,实现了对数据进行增删改查等接口。

 2.2 静态顺序表

#define N 100
//静态顺序表
struct SeqList
{
    int arr[N];//定长数组
    int size;//顺序表当前有效的数据个数
};

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

 2.3 动态顺序表 

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

  • 相较于静态顺序表,动态顺序表有个明显的优点——可以动态调整内存空间的大小(增容),它解决了静态顺序表的固定大小和内存浪费的问题。   

3. 顺序表的功能

顺序表的大致功能如下:

  1. 初始化顺序表
  2. 打印顺序表数据
  3. 对顺序表末尾插入/删除数据
  4. 对顺序表开头插入/删除数据 
  5. 对顺序表指定位置插入/删除数据 
  6. 对顺序表指定位置修改数据
  7. 查找顺序表数据
  8. 销毁顺序表 

4. 顺序表的定义

          首先,我们需要定义动态顺序表的数据类型SLDataType和结构体SeqList。动态顺序表使用数组arr存储数据,size表示当前有效数据个数,capacity表示当前容量大小。

typedef int SLDataType;//定义动态顺序表的数据类型
//动态顺序表结构体
typedef struct SeqList
{
    SLDataType* arr;//指向存储数据的数组
    int size;//当前有效数据个数
    int capacity;//当前容量大小
}SL;

5. 具体功能实现  

 5.1 初始化顺序表

        在进行初始化的时候,将顺序表的数组指针初始化为NULL,表示当前没有分配内存空间。同时,将顺序表的大小(size)容量(capacity)都初始化为0,表示当前顺序表中没有元素。

//顺序表的初始化
void SLIint(SL* ps)
{
    assert(ps);//断言
    ps->arr = NULL;
    ps->size = ps->capacity = 0;
}

5.2 打印顺序表数据

        打印数据前,先判断顺序表是否为空。若不为空,使用循环遍历顺序表中的元素,依次打印出来即可。

//打印顺序表元素
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");
}

5.3 对顺序表插入/删除数据

5.3.1 检查剩余空间大小

         对顺序表插入数据前,我们要检查顺序表的剩余空间大小是否足够插入新数据、是否需要扩容。

//检查剩余空间大小
void SLCheckCapacity(SL* ps)
{
    //插入数据之前先看空间够不够
    if (ps->capacity == ps->size)
    {
    	//申请空间
    	int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//三目操作符
    	SLDataType* ptr = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));//需要申请多大的空间
    	if (ptr == NULL)
    	{
    		perror("realloc fail!");
    		exit(1);//直接退出程序,不再继续执行
    	}
    	//空间申请成功
    	ps->arr = ptr;
    	ps->capacity = newCapacity;
    }
}

5.3.2 尾部插入数据 

        对顺序表尾部插入数据,再插入数据前先检查剩余空间大小是否足够插入新数据。

//尾插
void SLPushBack(SL* ps, SLDataType x)
{
    assert(ps);//断言
    SLCheckCapacity(ps);//检查剩余空间大小
    ps->arr[ps->size] = x;//尾部插入数据
    ps->size++;//有效数据个数+1
}

5.3.3 尾部删除数据

        同理,删除顺序表尾部的一个元素后,记得将有效数据个数-1。(注:在删除数据前要检查顺序表是否为空。

//尾删
void SLPopBack(SL* ps)
{
    assert(ps);//断言
    assert(ps->size);//顺序表不为空
    ps->size--;//删除数据,有效的数据个数减少
}

5.3.4 头部插入数据

         检查剩余空间的大小,使用循环将顺序表中的已有数据整体往后移一位,为新数据腾出位置。

//头插
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];
    }
    ps->arr[0] = x;
    ps->size++;//增加数据,有效的数据个数增加
}

5.3.5 头部删除数据

        与尾部删除数据不同,头部删除数据只需要将原有数据覆盖即可。

//头删
void SLPopFront(SL* ps)
{
    assert(ps);//断言
    assert(ps->size);//顺序表不为空
    //顺序表不为空
    for (int i = 0; i < ps->size; i++)
    {
    	ps->arr[i] = ps->arr[i + 1];
    }
    ps->size--;//删除数据,有效的数据个数减少
}

5.4 指定位置插入/删除数据 

5.4.1 指定位置插入数据

        和头插、尾插一样,指定位置插入数据前也需要判断是否需要扩容。

//指定位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
    assert(ps);//断言
    assert(ps->size);//顺序表不为空
    assert(pos >= 0 && pos <= ps->size);//检查下标pos的合法性
    SLCheckCapacity(ps);//扩容
    for (int i = ps->size; i > pos; i--)
    {
    	ps->arr[i] = ps->arr[i - 1];
    }
    ps->arr[pos] = x;
    ps->size++;//增加数据,有效的数据个数增多
}

5.4.2 指定位置删除数据

        与前边的删除数据大致相同,只是指定删除数据需要依次覆盖。

//指定位置删除数据
void SLErase(SL* ps, int pos)
{
    assert(ps);//断言
    assert(ps->size);//顺序表不为空
    assert(pos >= 0 && pos < ps->size);//检查下标pos的合法性
    for (int i = pos; i < ps->size - 1; i++)
    {
    	ps->arr[i] = ps->arr[i + 1];//将pos位置后面的数据依次向前移动一位
    }
	ps->size--;//删除数据,有效的数据个数减少
}

5.5 指定位置修改数据

        我们可以通过指定下标或者查找指定值的下标来修改任意位置的值。

//指定位置修改数据
void SLmodify(SL* ps, int pos, SLDataType x)
{
    assert(ps);//断言
    assert(ps->size);//顺序表不为空
    assert(pos >= 0 && pos < ps->size);//检查下标pos的合法性
    ps->arr[pos] = x;//修改下标为pos所对应的数据
}

5.6 查找顺序表数据 

        根据输入参数,在顺序表中查找指定的值并返回其下标,若未找到则返回-1。

//查找数据
int SLFind(SL* ps, SLDataType x)
{
    assert(ps);//断言
    assert(ps->size);//顺序表不为空
    for (int i = 0; i < ps->size; i++)
    {
     	if (ps->arr[i] == x)
    	{
    		return i;//查找到,返回该数据在数组中的下标值
    	}
    }
    return -1;//未查找到,返回值-1
}

5.7 销毁顺序表 

        在顺序表使用完毕后,应该及时回收顺序表所占用的内存空间,以避免内存泄漏和提高系统的内存利用率。

//顺序表的销毁
void SLDestroy(SL* ps)
{
    if (ps->arr != NULL) //检查顺序表的数据存储空间是否存在
    {
        free(ps->arr); //释放顺序表的数据存储空间
    }
    ps->arr = NULL; //将数据存储空间指针置为NULL
    ps->size = ps->capacity = 0; //将顺序表的大小和容量设为0
}

6. 完整代码 

        使用多个文件来组织和管理项目。将代码分散到多个文件中有助于提高代码的可读性、可维护性和可扩展性。

6.1 Seqlist.h

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

typedef int SLDataType;//定义动态顺序表的数据类型
//动态顺序表结构体
typedef struct SeqList
{
    SLDataType* arr;//指向存储数据的数组
    int size;//当前有效数据个数
    int capacity;//当前容量大小
}SL;
//typedef struct SeqList SL;

void SLIint(SL* ps);//初始化顺序表
void SLprint(SL* ps);//打印顺序表数据
 
void SLPushBack(SL* ps, SLDataType x);//尾插
void SLPopBack(SL* ps);//尾删
void SLPushFront(SL* ps, SLDataType x);//头插
void SLPopFront(SL* ps);//头删

void SLInsert(SL* ps, int pos, SLDataType x);//指定插入数据
void SLErase(SL* ps, int pos);//指定删除数据
void SLmodify(SL* ps, int pos, SLDataType x);//修改数据
int SLFind(SL* ps, SLDataType x);//查找顺序表数据

void SLDestory(SL* ps);//销毁顺序表,回收空间

6.2 Seqlist.c 

#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
//顺序表的初始化
void SLIint(SL* ps)
{
    assert(ps);//断言
    ps->arr = NULL;
    ps->size = ps->capacity = 0;
}

//打印顺序表元素
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 SLPushBack(SL* ps, SLDataType x)
{
    assert(ps);//断言
    SLCheckCapacity(ps);//检查剩余空间大小
    ps->arr[ps->size] = x;//尾部插入数据
    ps->size++;//有效数据个数+1
}

//尾删
void SLPopBack(SL* ps)
{
    assert(ps);//断言
    assert(ps->size);//顺序表不为空
    ps->size--;//删除数据,有效的数据个数减少
}

//头插
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];
    }
    ps->arr[0] = x;
    ps->size++;//增加数据,有效的数据个数增加
}

//头删
void SLPopFront(SL* ps)
{
    assert(ps);//断言
    assert(ps->size);//顺序表不为空
    //顺序表不为空
    for (int i = 0; i < ps->size; i++)
    {
    	ps->arr[i] = ps->arr[i + 1];
    }
    ps->size--;//删除数据,有效的数据个数减少
}

//指定位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
    assert(ps);//断言
    assert(ps->size);//顺序表不为空
    assert(pos >= 0 && pos <= ps->size);//检查下标pos的合法性
    SLCheckCapacity(ps);//扩容
    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(ps->size);//顺序表不为空
    assert(pos >= 0 && pos < ps->size);//检查下标pos的合法性
    for (int i = pos; i < ps->size - 1; i++)
    {
    	ps->arr[i] = ps->arr[i + 1];//将pos位置后面的数据依次向前移动一位
    }
    ps->size--;//删除数据,有效的数据个数减少
}

//指定位置修改数据
void SLmodify(SL* ps, int pos, SLDataType x)
{
    assert(ps);//断言
    assert(ps->size);//顺序表不为空
    assert(pos >= 0 && pos < ps->size);//检查下标pos的合法性
    ps->arr[pos] = x;//修改下标为pos所对应的数据
}

//查找数据
int SLFind(SL* ps, SLDataType x)
{
    assert(ps);//断言
    assert(ps->size);//顺序表不为空
    for (int i = 0; i < ps->size; i++)
    {
    	if (ps->arr[i] == x)
    	{
    		return i;//查找到,返回该数据在数组中的下标值
    	}
    }
    return -1;//未查找到,返回值-1
}

//顺序表的销毁
void SLDestroy(SL* ps)
{
    if (ps->arr != NULL) //检查顺序表的数据存储空间是否存在
    {
    	free(ps->arr); //释放顺序表的数据存储空间
    }
    ps->arr = NULL; //将数据存储空间指针置为NULL
    ps->size = ps->capacity = 0; //将顺序表的大小和容量设为0
}

7. 结语

        希望这篇文章对大家有所帮助,如果你有任何问题和建议,欢迎在评论区留言,这将对我有很大的帮助。

        完结!咻~

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值