【数据结构】顺序表

大家好!今天我们来学习数据结构中的顺序表。

目录

1. 线性表

2. 顺序表

2.1 顺序表的概念

2.2 顺序表的分类

3. 顺序表的定义

4. 顺序表的接口实现

4.1 顺序表的初始化

4.2 销毁顺序表

4.3 打印顺序表

4.4 检查顺序表是否需要扩容

4.5 尾插数据

​编辑

4.6 尾删数据

4.7 头插数据

​编辑

4.8 头删数据

4.9 在指定位置插入数据

4.10 删除指定位置的数据

4.11 查找指定数据

4.12 将指定位置的数据进行修改

5. 顺序表的完整代码

5.1 SeqList.h

5.2 SeqList.c

5.3 Test.c

 6. 总结


1. 线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使
用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线但是在物理结构上并不一定是连续的
线性表在物理上存储时,通常以数组链式结构的形式存储。
 

                                                               顺序表

                                                                链表

2. 顺序表

2.1 顺序表的概念

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组
储。在数组上完成数据的增删查改。

2.2 顺序表的分类

(1)静态顺序表:使用定长数组存储元素。

(2)动态顺序表:使用动态开辟的数组存储。

静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空
间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间
大小,所以下面我们实现动态顺序表。

3. 顺序表的定义

//动态顺序表
typedef int SLDataType;
#define INIT_CAPACITY 4typedef struct SeqList
{SLDataType* a; //指向动态开辟的数组int size;      //存储有效数据个数int capacity;  //空间大小
}SL;

使用结构体构造一个动态顺序表。

SLDataType替换int,方便对不同类型的数据进行修改。

SL替代struct SeqList, 方便简洁。

用宏定义INIT_CAPACITY顺序表的初始容量设置为4。

4. 顺序表的接口实现

顺序表的所有接口函数一览:

//顺序表的初始化
void SLInit(SL* ps);
//销毁顺序表
void SLDestroy(SL* ps);
//打印顺序表
void SLPrint(SL* ps);
//检查顺序表是否需要扩容
void SLCheckCapacity(SL* ps);//尾插
void SLPushBack(SL* ps, SLDataType x);
//尾删
void SLPopBack(SL* ps);
//头插
void SLPushFront(SL* ps, SLDataType x);
//头删
void SLPopFront(SL* ps);//返回下标,没有找到返回-1
int SLFind(SL* ps, SLDataType x);
//在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType x);
//删除pos位置的值
void SLErase(SL* ps, int pos);
//将pos位置的值修改为x
void SLModify(SL* ps, int pos, SLDataType x);

这些接口函数主要实现了顺序表的增删改查等功能,接下来我们一一实现这些函数!

4.1 顺序表的初始化

初始化就是我们给顺序表分配一个动态开辟的空间,将顺序表的元素个数置为0,将顺序表的初始空间大小置为我们宏定义的INIT_CAPACITY。

//初始化顺序表
void SLInit(SL* ps)
{assert(ps);ps->a = (SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY);if (ps->a == NULL){perror("malloc failed");exit(-1);  //让整个程序异常退出。//不是return,return只是让这个函数结束}ps->size = 0;ps->capacity = INIT_CAPACITY;
}

4.2 销毁顺序表

因为在实现动态顺序表的过程中,我们要使用动态内存分配的操作,如果不及时释放空间,会出现内存泄漏的问题。

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

4.3 打印顺序表

遍历顺序表,依次打印顺序表的元素。

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

4.4 检查顺序表是否需要扩容

因为顺序表中有可能已经放满元素了,那我们之后如果想插入数据,就得进行扩容的操作。

是否需要扩容,可以通过sizecapacity是否相等判断,如果相等,说明放满了。我们就要进行扩容操作。而进行扩容操作,我们就要使用扩容函数realloc函数。我们一般将空间扩容成原来的2倍(当然也可以是3倍,4倍,5倍...)

//检查是否需要扩容
void SLCheckCapacity(SL* ps)
{assert(ps);//满了要扩容if (ps->size == ps->capacity){SLDataType* tmp = (SLDataType*)realloc(ps->a, ps->capacity * sizeof(SLDataType) * 2);if (tmp == NULL){perror("realloc failed");exit(-1);}ps->a = tmp;ps->capacity *= 2;}
}

4.5 尾插数据

因为要插入数据,所以我们得检查一下是否满了,满了就要进行扩容操作

//尾插
void SLPushBack(SL* ps, SLDataType x)
{assert(ps);SLCheckCapacity(ps);ps->a[ps->size] = x;ps->size++;
}

4.6 尾删数据

尾删实现起来比较简单,就是将数组的长度减一,但是我们不能在顺序表是空(即size==0)的情况下删除数据。

我们得保证size>0的情况下才尾删,这里有两种检查方法:

(1)温柔的检查:使用if语句,如果size为0,直接return

(2)暴力的检查:如果size为0,直接用assert()断言

两种方法都可行,比较推荐第二种。

//尾删
void SLPopBack(SL* ps)
{assert(ps);//温柔的检查//if (ps->size == 0)//return;//暴力的检查assert(ps->size > 0);ps->size--;  //不能局部释放
}

4.7 头插数据

头插就是将数组的整体数据向后挪动一格,然后用指定数据替换第一个数据。

比如这里我们将6插入到第一个位置。

 但是注意在挪动的时候,我们要从后向前挪,因为如果从前往后挪,会导致数据的覆盖!!!

//头插
void SLPushFront(SL* ps,SLDataType x)
{assert(ps);SLCheckCapacity(ps);//挪动数据(从后往前挪,因为从前往后挪会覆盖)int end = ps->size - 1;while (end>=0){ps->a[end + 1] = ps->a[end];--end;}ps->a[0] = x;ps->size++;
}

4.8 头删数据

和头插类似,只不过头删是将数组的整体往前挪动一格。和头插不同的是,头删得从前往后挪动,不然也会出现覆盖问题。

//头删
void SLPopFront(SL* ps)
{assert(ps);assert(ps->size > 0);int begin = 1;while (begin < ps->size){ps->a[begin - 1] = ps->a[begin];++begin;}ps->size--;}

4.9 在指定位置插入数据

将指定位置pos后面的所有数据整体往后挪动一格,再给指定位置pos赋新值,要保证指定位置pos的合理性和是否放满了,要从后往前挪动

//在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);int end = ps->size - 1;while (end >= pos){ps->a[end + 1] = ps->a[end];--end;}ps->a[pos] = x;ps->size++;
}

头插代码可以复用这个代码:

void SLPushFront(SL* ps,SLDataType x)
{assert(ps);SLInsert(ps, 0, x);
}

尾插代码可以复用这个代码:

void SLPushBack(SL* ps, SLDataType x)
{assert(ps);SLInsert(ps, ps->size, x);
}

4.10 删除指定位置的数据

将指定位置后面的所有数据整体往前挪动一格,要保证指定位置pos的合理性,要从前往后挪动,同时让size减一。

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

头删代码可以复用这个代码:

void SLPopFront(SL* ps)
{assert(ps);SLErase(ps, 0);
}

尾删代码可以复用这个代码:

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

4.11 查找指定数据

查找的代码实现起来非常简单,找到了就返回下标,找不到返回-1。

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

4.12 将指定位置的数据进行修改

在保证pos合理性的情况下,对pos位置的数据可以进行修改。

//将pos位置的值修改为x
void SLModify(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos < ps->size);ps->a[pos] = x;
}

5. 顺序表的完整代码

5.1 SeqList.h

#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//动态顺序表
typedef int SLDataType;
#define INIT_CAPACITY 4typedef struct SeqList
{SLDataType* a; //指向动态开辟的数组int size;      //存储有效数据个数int capacity;  //空间大小
}SL;//顺序表的初始化
void SLInit(SL* ps);
//销毁顺序表
void SLDestroy(SL* ps);
//打印顺序表
void SLPrint(SL* ps);
//检查顺序表是否需要扩容
void SLCheckCapacity(SL* ps);//尾插
void SLPushBack(SL* ps, SLDataType x);
//尾删
void SLPopBack(SL* ps);
//头插
void SLPushFront(SL* ps, SLDataType x);
//头删
void SLPopFront(SL* ps);//返回下标,没有找到返回-1
int SLFind(SL* ps, SLDataType x);
//在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType x);
//删除pos位置的值
void SLErase(SL* ps, int pos);
//将pos位置的值修改为x
void SLModify(SL* ps, int pos, SLDataType x);

5.2 SeqList.c

#define _CRT_SECURE_NO_WARNINGS 1#include"SeqList.h"//初始化顺序表
void SLInit(SL* ps)
{assert(ps);ps->a = (SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY);if (ps->a == NULL){perror("malloc failed");exit(-1);  //让整个程序异常退出。//不是return,return只是让这个函数结束}ps->size = 0;ps->capacity = INIT_CAPACITY;
}//销毁顺序表
void SLDestroy(SL* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->capacity = ps->size = 0;
}//打印顺序表
void SLPrint(SL* ps)
{assert(ps);for (int i = 0; i < ps->size; i++){printf("%d ", ps->a[i]);}printf("\n");
}//检查是否需要扩容
void SLCheckCapacity(SL* ps)
{assert(ps);//满了要扩容if (ps->size == ps->capacity){SLDataType* tmp = (SLDataType*)realloc(ps->a, ps->capacity * sizeof(SLDataType) * 2);if (tmp == NULL){perror("realloc failed");exit(-1);}ps->a = tmp;ps->capacity *= 2;}
}//尾插
void SLPushBack(SL* ps, SLDataType x)
{assert(ps);SLCheckCapacity(ps);ps->a[ps->size] = x;ps->size++;
}//尾删
void SLPopBack(SL* ps)
{assert(ps);//温柔的检查//if (ps->size == 0)//return;//暴力的检查assert(ps->size > 0);ps->size--;  //不能局部释放
}//头插
void SLPushFront(SL* ps,SLDataType x)
{assert(ps);SLCheckCapacity(ps);//挪动数据(从后往前挪,因为从前往后挪会覆盖)int end = ps->size - 1;while (end>=0){ps->a[end + 1] = ps->a[end];--end;}ps->a[0] = x;ps->size++;
}//头删
void SLPopFront(SL* ps)
{assert(ps);assert(ps->size > 0);int begin = 1;while (begin < ps->size){ps->a[begin - 1] = ps->a[begin];++begin;}ps->size--;
}//在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);int end = ps->size - 1;while (end >= pos){ps->a[end + 1] = ps->a[end];--end;}ps->a[pos] = x;ps->size++;
}//删除pos位置的值
void SLErase(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);int begin = pos + 1;while (begin < ps->size){ps->a[begin - 1] = ps->a[begin];++begin;}ps->size--;
}//查找
int SLFind(SL* ps, SLDataType x)
{assert(ps);for (int i = 0; i < ps->size; i++){if (ps->a[i] == x)return i;}return -1;
}//将pos位置的值修改为x
void SLModify(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos < ps->size);ps->a[pos] = x;
}

5.3 Test.c

#define _CRT_SECURE_NO_WARNINGS 1#include"SeqList.h"void TestSeqList()
{SL sl;SLInit(&sl);SLPushBack(&sl, 1);SLPushBack(&sl, 2);SLPushBack(&sl, 3);SLPushBack(&sl, 4);SLPushBack(&sl, 5);SLPushBack(&sl, 6);SLPushBack(&sl, 6);SLPrint(&sl);SLPopBack(&sl);SLPopBack(&sl);SLPrint(&sl);SLPushFront(&sl , 0);SLPushFront(&sl, -1);SLPrint(&sl);SLPopFront(&sl);SLPrint(&sl);int x;printf("请选择要查询的数字:>");scanf("%d", &x);int pos = SLFind(&sl, x);if (pos != -1){printf("x=%d的下标是%d\n", x,pos);}elseprintf("x不存在\n");SLInsert(&sl, 3, 8);SLPrint(&sl);SLErase(&sl, 4);SLPrint(&sl);SLModify(&sl, 1, 10);SLPrint(&sl);SLDestroy(&sl);
}int main()
{TestSeqList();return 0;
}

 6. 总结

到这里,我们就用C语言实现了动态顺序表。有什么问题欢迎在评论区讨论。如果觉得文章有什么不足之处,可以在评论区留言。如果喜欢我的文章,可以点赞收藏哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/26192.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

postgresql|数据库|MySQL数据库向postgresql数据库迁移的工具pgloader的部署和初步使用

前言&#xff1a; MySQL数据库和postgresql数据库之间的差异并不多&#xff0c;这里的差异指的是对SQL语言的支持两者并不大&#xff0c;但底层的东西差异是非常多的&#xff0c;例如&#xff0c;MySQL的innodb引擎概念&#xff0c;数据库用户管理&#xff0c;这些和postgresq…

3.2 防火墙

数据参考&#xff1a;CISP官方 目录 防火墙基础概念防火墙的典型技术防火墙企业部署防火墙的局限性 一、防火墙基础概念 防火墙基础概念&#xff1a; 防火墙&#xff08;Firewall&#xff09;一词来源于早期的欧式建筑&#xff0c;它是建筑物之间的一道矮墙&#xff0c;用…

static关键字

作者简介&#xff1a; zoro-1&#xff0c;目前大一&#xff0c;正在学习Java&#xff0c;数据结构等 作者主页&#xff1a; zoro-1的主页 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f496; 被static修饰意味什么 在Java中&#xff0c;被st…

STM32 CubeMX USB_(HID 鼠标和键盘)

STM32 CubeMX STM32 CubeMX USB_HID&#xff08;HID 鼠标和键盘&#xff09; STM32 CubeMX前言 《鼠标》一、STM32 CubeMX 设置USB时钟设置USB使能UBS功能选择 二、代码部分添加代码鼠标发送给PC的数据解析实验效果 《键盘》STM32 CubeMX 设置&#xff08;同上&#xff09;代码…

239. 滑动窗口最大值

1 问题描述 2 思路 为了每次元素进出的时候&#xff0c;我们都可以得到该窗口内的最大元素&#xff0c;我们需要自己定义一种队列里面包含方法可以获取最大值怎么才能获取队列里的最大值呢&#xff1f;在队列里排序也可以&#xff0c;这里我们采用单调队列&#xff1a;该队列…

innovus gui界面文字大小和对话框大小调整

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f;拾陆楼知识星球入口 uiApp::setfont -dialog

【Docker】Docker容器化技术基础

Docker容器化技术 Docker&#xff08;软件跨环境迁移&#xff09;Docker概念&#xff1a;安装Dockerdocker架构配置Docker镜像加速器 一、Docker命令服务daemon相关的命令镜像相关命令Docker容器相关命令 二、Docker容器的数据卷数据卷概念配置数据卷配置数据卷容器 三、Docker…

Windows系统下添加了新环境变量无需重启电脑激活新环境变量的方法

首先WinR&#xff0c;再输入cmd&#xff0c;进入终端&#xff0c;输入以下命令&#xff1a; set Pathc输入完以上命令回车&#xff0c;如下&#xff1a; 关闭终端后再次打开输入cl&#xff0c;如果输出以下类似信息说明新的环境变量已经添加成功&#xff0c;如下&#xff1a; …

了解HTTP代理日志:解读请求流量和响应信息

嗨&#xff0c;爬虫程序员们&#xff01;你们是否在了解爬虫发送的请求流量和接收的响应信息上有过困扰&#xff1f;今天&#xff0c;我们一起来了解一下。 首先&#xff0c;我们需要理解HTTP代理日志的基本结构和内容。HTTP代理日志是对爬虫发送的请求和接收的响应进行记录的文…

C语言假期作业 DAY 15

一、选择题 1、有如下代码&#xff0c;则 *(p[0]1) 所代表的数组元素是&#xff08; &#xff09; int a[3][2] {1, 2, 3, 4, 5, 6}, *p[3]; p[0] a[1]; A: a[0][1] B: a[1][0] C: a[1][1] D: a[1][2] 答案解析 正确答案&#xff1a; C p 是一个指针数组&#xff0c; p[0] a…

SSM项目-博客系统

在线体验项目&#xff1a;登陆页面 项目连接&#xff1a;huhublog_ssm: 个人博客系统 技术栈&#xff1a;SpringBoot、SpringMVC、Mybatis、Redis、JQuery、Ajax、Json (gitee.com) 1.项目技术点分析 SpringBoot、SpringWeb(SpringMVC)、MyBatis、MySQL(8.x)、Redis(存储验…

kubernetes 集群利用 efk 收集容器日志

文章目录 [toc]前情提要制作 centos 基础镜像准备 efk 二进制文件部署 efk 组件配置 namespace配置 gfs 的 endpoints配置 pv 和 pvc部署 elasticsearchefk-cmefk-svcefk-sts 部署 filebeatfilebeat-cmfilebeat-ds 部署 kibanakibana-cmkibana-svckibana-dp使用 nodeport 访问 …

CAPL - XML和TestModule结合实现测试项可选

目录 目的:是否想实现如下面的功能呢? 一、.can和.cin文件中函数开发

Java Selenium WebDriver 网页填报

一、windows环境安装配置 1.安装chrome浏览器 在“关于chrome”界面&#xff0c;查看浏览器版本号 2.下载chromeDriver 在https://registry.npmmirror.com/binary.html?pathchromedriver/下载对应版本的驱动&#xff08;如果浏览器版本过新&#xff0c;建议下载最接近的版…

STM32基础入门学习笔记:内部高级功能应用

文章目录&#xff1a; 一&#xff1a;低功耗模式 1.睡眠模式测试程序 NVIC.h NVIC.c key.h key.c main.c 2.停机模式测试程序 main.c 3.待机模式测试程序 main.c 二&#xff1a;看门狗 1.独立看门狗测试程序 iwdg.h iwdg.c main.c 2.窗口看门狗测试程序 wwdg…

Linux网络服务之部署yum仓库

yum &#xff1f; yum ! 一、YUM概述1.1 yum简介1.2 yum工作原理 二、yum 配置文件2.1 yum主配置文件2.2 yum仓库设置文件2.2.1 配置文件主要格式2.2.2 软件仓库的提供方式2.2.3 日志文件 三、yum命令详解3.1 安装和升级3.2 查询3.2.1 显示可用的安装包 ----- yum list3.2.2 显…

Python程序设计基础:函数(一)

文章目录 一、函数的基本概念二、函数的定义和使用1、函数的定义与调用2、函数的参数3、返回多个值 一、函数的基本概念 在使用Python实现某些复杂的功能的时候&#xff0c;容易遇到一些重复率较高的代码&#xff0c;为了代码能够重复使用并提升代码的整洁度&#xff0c;函数这…

Vulnhub: ColddWorld: Immersion靶机

kali&#xff1a;192.168.111.111 靶机&#xff1a;192.168.111.183 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.183 查看login的源码发现提示&#xff1a;page和文件/var/carls.txt 漏洞利用 wfuzz探测account.php页面发现文件包含&am…

Excel中——日期列后添加星期

需求&#xff1a;在日期列中添加星期几&#xff1f; 第一步&#xff1a;打开需要添加星期的Excel文件&#xff0c;在日期后面添加日期 第二步&#xff1a;选择日期列&#xff0c;点击鼠标右键&#xff0c;在下拉列表中&#xff0c;选择“设置单元格格式” 第三步&#xff1a; 在…

JVM基础篇-StringTable

StringTable 特性 常量池中的字符串仅是符号&#xff0c;第一次用到时才变为对象 利用串池的机制&#xff0c;来避免重复创建字符串对象 字符串变量拼接的原理是 StringBuilder &#xff08;1.8&#xff09; 字符串常量拼接的原理是编译期优化 可以使用 intern 方法&#…