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

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

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

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

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

目录

引言

[1. 什么是顺序表?](#1. 什么是顺序表?)

[2. 顺序表的分类](#2. 顺序表的分类)

[2.1 顺序表和数组的区别](#2.1 顺序表和数组的区别)

[2.2 静态顺序表](#2.2 静态顺序表)

[2.3 动态顺序表](#2.3 动态顺序表)

[3. 顺序表的功能](#3. 顺序表的功能)

[4. 顺序表的定义](#4. 顺序表的定义)

[5. 具体功能实现](#5. 具体功能实现)

[5.1 初始化顺序表](#5.1 初始化顺序表)

[5.2 打印顺序表数据](#5.2 打印顺序表数据)

[5.3 对顺序表插入/删除数据](#5.3 对顺序表插入/删除数据)

[5.3.1 检查剩余空间大小](#5.3.1 检查剩余空间大小)

[5.3.2 尾部插入数据](#5.3.2 尾部插入数据)

[5.3.3 尾部删除数据](#5.3.3 尾部删除数据)

[5.3.4 头部插入数据](#5.3.4 头部插入数据)

[5.3.5 头部删除数据](#5.3.5 头部删除数据)

[5.4 指定位置插入/删除数据](#5.4 指定位置插入/删除数据)

[5.4.1 指定位置插入数据](#5.4.1 指定位置插入数据)

[5.4.2 指定位置删除数据](#5.4.2 指定位置删除数据)

[5.5 指定位置修改数据](#5.5 指定位置修改数据)

[5.6 查找顺序表数据](#5.6 查找顺序表数据)

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

[6. 完整代码](#6. 完整代码)

[6.1 Seqlist.h](#6.1 Seqlist.h)

[6.2 Seqlist.c](#6.2 Seqlist.c)

[7. 结语](#7. 结语)


引言

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

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


1. 什么是顺序表?

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

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


2. 顺序表的分类

2.1 顺序表和数组的区别

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

2.2 静态顺序表

cpp 复制代码
#define N 100
//静态顺序表
struct SeqList
{
    int arr[N];//定长数组
    int size;//顺序表当前有效的数据个数
};
  • 静态顺序表缺陷:空间给少了不够用,给多了造成空间浪费

2.3 动态顺序表

cpp 复制代码
//动态顺序表
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表示当前容量大小。

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

5. 具体功能实现

5.1 初始化顺序表

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

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

5.2 打印顺序表数据

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

cpp 复制代码
//打印顺序表元素
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 检查剩余空间大小

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

cpp 复制代码
//检查剩余空间大小
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 尾部插入数据

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

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

5.3.3 尾部删除数据

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

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

5.3.4 头部插入数据

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

cpp 复制代码
//头插
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 头部删除数据

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

cpp 复制代码
//头删
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 指定位置插入数据

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

cpp 复制代码
//指定位置插入数据
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 指定位置删除数据

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

cpp 复制代码
//指定位置删除数据
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 指定位置修改数据

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

cpp 复制代码
//指定位置修改数据
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。

cpp 复制代码
//查找数据
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 销毁顺序表

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

cpp 复制代码
//顺序表的销毁
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

cpp 复制代码
#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

cpp 复制代码
#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. 结语

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

完结!咻~

相关推荐
盼海1 小时前
排序算法(五)--归并排序
数据结构·算法·排序算法
搬砖的小码农_Sky7 小时前
C语言:数组
c语言·数据结构
朝九晚五ฺ7 小时前
【Linux探索学习】第十四弹——进程优先级:深入理解操作系统中的进程优先级
linux·运维·学习
先鱼鲨生9 小时前
数据结构——栈、队列
数据结构
一念之坤9 小时前
零基础学Python之数据结构 -- 01篇
数据结构·python
猫爪笔记9 小时前
前端:HTML (学习笔记)【1】
前端·笔记·学习·html
IT 青年9 小时前
数据结构 (1)基本概念和术语
数据结构·算法
熬夜学编程的小王9 小时前
【初阶数据结构篇】双向链表的实现(赋源码)
数据结构·c++·链表·双向链表
pq113_69 小时前
ftdi_sio应用学习笔记 3 - GPIO
笔记·学习·ftdi_sio
澄澈i10 小时前
设计模式学习[8]---原型模式
学习·设计模式·原型模式