【数据结构】线性表之《顺序表》超详细实现

顺序表

  • 一.数据结构
    • 1.逻辑结构
    • 2.物理结构
  • 二.顺序表的分类
    • 1.静态顺序表
    • 2.动态顺序表
  • 三.顺序表的实现
    • 1.创建顺序表
    • 2.初始化顺序表
    • 3.判断是否扩容
    • 4.打印顺序表
    • 5.插入操作
      • 1.头插
      • 2.尾插
      • 3.按照下标插入
    • 6.删除操作
      • 1.头删
      • 2.尾删
      • 3.按照下标删除
    • 7.查找数据
    • 8.修改数据
    • 9.清空顺序表
    • 10.销毁顺序表
  • 四.模块化源代码
    • 1.SeqList.h
    • 2.SeqList.c
    • 3.test.c
  • 五.顺序表必做OJ题

前言:相信大家在学完C语言后,对于指针结构体以及动态内存管理有了一定的见解,那么数据结构像是检验真理的唯一标椎,它考察了你掌握这三种语法的程度,如果掌握的不是很理想的友友们,建议看看鄙人主页总结的文章,好啦,数据结构之旅就正式开始啦。

一.数据结构

数据结构计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。数据结构反映数据的内部构成,即数据由那部分构成,以什么方式构成,以及数据元素之间呈现的结构。

1.逻辑结构

逻辑结构:数据对象中数据元素之间的相互关系。
逻辑结构分为:集合结构,线性结构,树形结构和图形结构。

2.物理结构

物理结构:数据的逻辑结构在计算机中的存储形式。
物理结构分为:顺序存储结构和链式存储结构。

二.顺序表的分类

顺序表实际属于线性表这个大家族,那么什么是线性表呢?

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

1.静态顺序表

#define MAXSIZE 100     //静态顺序表的容量
typedef int SLDataType; //增强程序的可维护性typedef struct SeqList
{SLDataType arr[MAXSIZE];//定长数组int size;               //有效的数据个数
}SeqList;

也许有人会问:这个静态顺序表说的这么神秘,本质上不就是一个数组吗?

答:没错,顺序表的低层就是数组,逻辑结构与物理结构都是连续的。不过顺序表对数组进行了封装,提供了大量常用的接口(所谓接口就是调用函数),方便完成数据进行增,删,查,改等一系列的操作。

静态顺序表的缺点:程序一但运行,数组的大小就确定了,不能被修改。数组大小给小了,空间就不够用;数组大小给大了,空间就白白的浪费掉了;

为了方便对静态顺序表进行扩容,于是,动态顺序表便应运而生。

2.动态顺序表

typedef int SLDataType;//增强程序的可维护性typedef struct SeqList
{SLDataType* arr; //动态的,可以扩容int size;        //有效的数据个数int capacity;    //容量
}SL;

动态顺序表:通过堆区的动态内存管理控制顺序表空间的大小,即容量。

以下介绍的是基于动态顺序表的实现。

三.顺序表的实现

1.创建顺序表

创建一个顺序表结构,包含指向这块空间的起始地址,顺序表中存储数据的个数以及顺序表的最大容量。

typedef int SLDataType;//增强程序的可维护性,改变数据类型,只需改变int即可typedef struct SeqList
{SLDataType* arr; //指向顺序表的起始位置,方便扩容int size;        //顺序表的有效的数据个数int capacity;    //顺序表的容量大小
}SL;//类型的重命名
//等价于typedef struct SeqList SL;

2.初始化顺序表

一般初始化我们都习惯赋值为0,指针赋值为NULL。

void SeqListInit(SL* ps)
{assert(ps);//断言操作,等价于assert(ps != NULL);ps->arr = NULL;ps->size = 0;ps->capacity = 0;
}

3.判断是否扩容

当我们想要增加数据的时候,会遇到容量不够用的问题。所以在此之前,应该检查容量与有效数据之间的关系,判断是否要扩容,接着插入数据。

void SeqListCheckCapacity(SL* ps)
{assert(ps);if (ps->size == ps->capacity)//顺序表已满,进行扩容操作{//判断顺序表容量是否为0,若为0,开辟储存4个数据大小的空间,若不为0,则容量翻倍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;//修改容量}
}

注意:若传入realloc的指针为空指针(NULL),则realloc函数等价于malloc函数。

4.打印顺序表

循环遍历顺序表数据即可。

void SeqListPrint(SL* ps)
{assert(ps);for (int i = 0; i < ps->size; i++){printf("%d ", ps->arr[i]);}printf("\n");
}

5.插入操作

1.头插

头插的思想:将数据整体向后挪动一位。注意:为了防止覆盖原先的数据,采用从后向前挪动。再进行头部插入,最后有效数据个数+1。

void SeqListPushFront(SL* ps, SLDataType x)
{assert(ps);SeqListCheckCapacity(ps);//检查容量int end = ps->size;while (end > 0){ps->arr[end] = ps->arr[end - 1];//将数据整体向后挪动一位。end--;}ps->arr[0] = x;//头插ps->size++;//有效数据个数+1//等价于在下标为0处插入:SeqListInsert(ps, 0, x);
}

2.尾插

尾插的思想:直接在尾部插入,有效数据个数+1即可。

void SeqListPushBack(SL* ps, SLDataType x)
{assert(ps);SeqListCheckCapacity(ps);//检查容量ps->arr[ps->size++] = x;//尾插:采用后置++//等价于在下标为ps->size处插入:SeqListInsert(ps, ps->size, x);
}

3.按照下标插入

按照下标插入的思想:与头插的思想相同,只不过是要挪动数据的起始位置不同而已。

void SeqListInsert(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);//判断下标的合法性SeqListCheckCapacity(ps);int end = ps->size;while (end > pos){ps->arr[end] = ps->arr[end - 1];end--;}ps->arr[pos] = x;ps->size++;
}

6.删除操作

1.头删

头删的思想:不是真正意义上的删除数据,而是将头部之后的数据整体向前挪动一位,覆盖头部数据,达到与头删相同的效果,最后有效数据个数-1。注意:为了防止覆盖原先的数据,采用从前向后挪动,这里与头插不同。

void SeqListPopFront(SL* ps)
{assert(ps);assert(ps->size > 0);//前提:顺序表存在数据int start = 0;while (start < ps->size - 1){ps->arr[start] = ps->arr[start + 1];start++;}ps->size--;//有效数据个数-1//等价于在下标为0处删除:SeqListErase(ps, 0);
}

2.尾删

尾删的思想:有效数据个数-1即可。

void SeqListPopBack(SL* ps)
{assert(ps);assert(ps->size > 0);//前提:顺序表存在数据ps->size--;//有效数据个数-1//等价于在下标为ps->size - 1处删除:SeqListErase(ps, ps->size - 1);
}

3.按照下标删除

按照下标删除的思想:与头删的思想相同,只不过是要挪动数据的起始位置不同而已。

void SeqListErase(SL* ps, int pos)
{assert(ps);assert(ps->size > 0);//前提:顺序表存在数据assert(pos >= 0 && pos < ps->size);//判断下标的合法性int start = pos;while (start < ps->size - 1){ps->arr[start] = ps->arr[start + 1];start++;}ps->size--;
}

7.查找数据

查找的思想:遍历数据,找到了返回下标,未找到返回-1即可。

int SeqListFind(SL* ps, SLDataType x)
{assert(ps);for (int i = 0; i < ps->size; i++){if (ps->arr[i] == x){return i;//找到该数据,返回下标}}return -1;//未找到,返回-1
}

8.修改数据

修改的思想:利用下标确定位置,直接修改就行。较为简单。

void SeqListModify(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos < ps->size);ps->arr[pos] = x;
}

9.清空顺序表

清空的思想:将有效数据个数与容量赋值为0。

void SeqListClear(SL* ps)
{assert(ps);ps->size = 0;ps->capacity = 0;
}

10.销毁顺序表

销毁的思想:由于空间是利用动态内存函数 realloc 在堆区开辟的,所以要及时释放,避免内存泄漏。

void SeqListDestory(SL* ps)
{assert(ps);if (ps->arr != NULL){free(ps->arr);}ps->arr = NULL;//置为NULL,防止野指针SeqListClear(ps);//清空顺序表
}

四.模块化源代码

1.SeqList.h

//#pragma once:防止头文件被重复包含
#ifndef __SEQLIST_H__
#define __SEQLIST_H__#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>typedef int SLDataType;//增强程序的可维护性typedef struct SeqList
{SLDataType* arr; //动态的,可以扩容int size;        //有效的数据个数int capacity;    //容量
}SL;void SeqListInit(SL* ps);//顺序表初始化void SeqListCheckCapacity(SL* ps);//检查顺序表的容量void SeqListPrint(SL* ps);//打印顺序表void SeqListPushBack(SL* ps, SLDataType x);//顺序表尾部插入void SeqListPushFront(SL* ps, SLDataType x);//顺序表头部插入void SeqListPopBack(SL* ps);//顺序表尾部删除void SeqListPopFront(SL* ps);//顺序表头部删除void SeqListInsert(SL* ps, int pos, SLDataType x);//顺序表按照位置(下标)插入void SeqListErase(SL* ps, int pos);//顺序表按照位置(下标)删除int SeqListFind(SL* ps, SLDataType x);//顺序表查找元素值,返回下标void SeqListModify(SL* ps, int pos, SLDataType x);//顺序表按位置(下标)修改void SeqListClear(SL* ps);//清空顺序表void SeqListDestory(SL* ps);//销毁顺序表#endif

2.SeqList.c

#define _CRT_SECURE_NO_WARNINGS 1#include"SeqList.h"void SeqListInit(SL* ps)
{assert(ps);//等价于assert(ps != NULL);ps->arr = NULL;ps->size = 0;ps->capacity = 0;
}void SeqListCheckCapacity(SL* ps)
{assert(ps);if (ps->size == ps->capacity){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 SeqListPrint(SL* ps)
{assert(ps);for (int i = 0; i < ps->size; i++){printf("%d ", ps->arr[i]);}printf("\n");
}void SeqListPushBack(SL* ps, SLDataType x)
{assert(ps);SeqListCheckCapacity(ps);ps->arr[ps->size++] = x;//SeqListInsert(ps, ps->size, x);
}void SeqListPushFront(SL* ps, SLDataType x)
{assert(ps);SeqListCheckCapacity(ps);int end = ps->size;while (end > 0){ps->arr[end] = ps->arr[end - 1];end--;}ps->arr[0] = x;ps->size++;//SeqListInsert(ps, 0, x);
}void SeqListPopBack(SL* ps)
{assert(ps);assert(ps->size > 0);ps->size--;//SeqListErase(ps, ps->size - 1);
}void SeqListPopFront(SL* ps)
{assert(ps);assert(ps->size > 0);int start = 0;while (start < ps->size - 1){ps->arr[start] = ps->arr[start + 1];start++;}ps->size--;//SeqListErase(ps, 0);
}void SeqListInsert(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);SeqListCheckCapacity(ps);int end = ps->size;while (end > pos){ps->arr[end] = ps->arr[end - 1];end--;}ps->arr[pos] = x;ps->size++;
}void SeqListErase(SL* ps, int pos)
{assert(ps);assert(ps->size > 0);assert(pos >= 0 && pos < ps->size);int start = pos;while (start < ps->size - 1){ps->arr[start] = ps->arr[start + 1];start++;}ps->size--;
}int SeqListFind(SL* ps, SLDataType x)
{assert(ps);for (int i = 0; i < ps->size; i++){if (ps->arr[i] == x){return i;}}return -1;
}void SeqListModify(SL* ps, int pos, SLDataType x)
{assert(ps);assert(pos >= 0 && pos < ps->size);ps->arr[pos] = x;
}void SeqListClear(SL* ps)
{assert(ps);ps->size = 0;ps->capacity = 0;
}void SeqListDestory(SL* ps)
{assert(ps);if (ps->arr != NULL){free(ps->arr);}ps->arr = NULL;SeqListClear(ps);
}

3.test.c

#define _CRT_SECURE_NO_WARNINGS 1#include"SeqList.h"enum//匿名枚举
{EXIT,PUSHBACK,PUSHFRONT,POPBACK,POPFRONT,INSERT,ERASE,FIND,MODIFY,PRINT,CLEAR
};void Menu()
{printf("*************顺序表************\n");printf("****1.尾插           2.头插****\n");printf("****3.尾删           4.头删****\n");printf("****5.插入           6.删除****\n");printf("****7.查找           8.修改****\n");printf("****9.打印          10.清空****\n");printf("****0.退出               ******\n");printf("*******************************\n");
}int main()
{SL sl;SeqListInit(&sl);int select = 0;   //菜单选项SLDataType value; //插入的值int pos = 0;      //插入或删除的下标do{Menu();printf("请选择您的操作:");scanf("%d", &select);switch (select){case EXIT:printf("退出顺序表!");break;case PUSHBACK:printf("请输入您要尾插的值(输入-1代表结束):");while ((scanf("%d", &value), value != -1)) //逗号表达式{SeqListPushBack(&sl, value);}break;case PUSHFRONT:printf("请输入您要头插的值(输入-1代表结束):");do{scanf("%d", &value);if (value != -1){SeqListPushFront(&sl, value);}} while (value != -1);break;case POPBACK:SeqListPopBack(&sl);break;case POPFRONT:SeqListPopFront(&sl);break;case INSERT:printf("请输入要插入的《下标》与《值》:");scanf("%d %d", &pos, &value);SeqListInsert(&sl, pos, value);break;case ERASE:printf("请输入要删除的《下标》:");scanf("%d", &pos);SeqListErase(&sl, pos);break;case FIND:printf("请输入要查找的《值》:");scanf("%d", &value);int ret = SeqListFind(&sl, value);if (ret != -1){printf("找到了下标为:%d\n", ret);}else{printf("找不到!");}break;case MODIFY:printf("请输入要修改的《下标》以及修改后的《值》:");scanf("%d %d", &pos, &value);SeqListModify(&sl, pos, value);break;case PRINT:SeqListPrint(&sl);break;case CLEAR:SeqListClear(&sl);break;default:printf("您输入的值错误,请重新选择!\n");break;}} while (select);SeqListDestory(&sl);return 0;
}

五.顺序表必做OJ题

  1. 原地移除元素
  2. 合并两个有序数组

创作不易,如果能帮到你的话能赏个三连吗?感谢啦!!!

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

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

相关文章

【文末附gpt升级秘笈】OpenAI CTO采访揭底:GPT-4是否已触及大模型能力瓶颈?

OpenAI CTO采访揭底&#xff1a;GPT-4是否已触及大模型能力瓶颈&#xff1f; 一、引言 近年来&#xff0c;人工智能&#xff08;AI&#xff09;领域的发展日新月异&#xff0c;其中大型语言模型&#xff08;LLM&#xff09;如GPT系列更是引领了新一轮的技术浪潮。然而&#x…

从11个视角看全球Rust程序员2/4:深度解读JetBrains最新报告

讲动人的故事,写懂人的代码 5 Rust代码最常使用什么协议与其他代码交互? REST API: 2022年:51%2023年:51%看上去REST API的使用比例挺稳定的,没啥变化。语言互操作性(Language Interop): 2022年:53%2023年:43%语言互操作性的比例在2023年下来了一些,掉了10个百分点…

示例:WPF中DataGrid设置多级分组样式

一、目的&#xff1a;应用CollectionViewSource和GroupStyle设置DataGrid多级分组样式 二、实现 一级分组效果如下 二级分组效果如下 三、环境 VS2022 四、示例 具体实现代码如下 <TabItem Header"DataGrid - 多级分组"><TabItem.Resources><Colle…

ansible-Role角色批量按照node_export节点,并追加信息到Prometheus文件中

文章目录 剧本功能 inventory.yaml文件定义deploy.yaml角色定义node_exporter_lock角色定义任务角色main.yamlnode_exporter_tasks.yml角色触发任务notifyextra_tasks.yml角色prometheus_node_config.j2模板文件 执行命令查看变量 剧本功能 功能1&#xff1a; 批量执行node_ex…

Java数据结构与算法(多重背包)

前言: 多重背包问题&#xff08;Multiple Knapsack Problem, MKP&#xff09;是背包问题的一个变种。在这种问题中&#xff0c;你有多个背包&#xff0c;每个背包都有一定的容量。你需要选择一些物品放入这些背包中&#xff0c;以使放入物品的总价值最大化。每个物品都有一个价…

【Nvidia+AI摄像头】面向机器人双目视觉相机

随着人工智能和机器人技术的不断发展&#xff0c;双目深度相机作为一种重要的传感器&#xff0c;正在被广泛应用于各种机器人系统中。双目深度相机作为机器人不可或缺的感知器件&#xff0c;其高精度深度信息为机器人提供环境感知、立体视觉、姿态识别等功能&#xff0c;让机器…

Flink 窗口函数

一、Window 概述 Flink 流式计算是一种被设计用于处理无限数据集的数据处理引擎&#xff0c;而无限数据集是指一种不断增长的本质上无限的数据集&#xff0c;而 window 是一种切割无线数据为有限块进行处理的手段。 二、Window 分类 Window 可以分为两类&#xff1a; Count…

程序员画图工具?那必然是你了!!【送源码】

作为一个程序员&#xff0c;画图是必不可少的技巧。当然此画图不是搞艺术&#xff0c;而是画各种架构图、流程图、泳道图以及各种示意图。 平时我不论是记笔记、写技术文章&#xff0c;还是工作中写文档&#xff0c;都需要配上各种各样的示意图。不管是帮助自己更好的掌握知识…

云动态摘要 2024-06-17

给您带来云厂商的最新动态&#xff0c;最新产品资讯和最新优惠更新。 最新优惠与活动 [低至1折]腾讯混元大模型产品特惠 腾讯云 2024-06-06 腾讯混元大模型产品特惠&#xff0c;新用户1折起&#xff01; 云服务器ECS试用产品续用 阿里云 2024-04-14 云服务器ECS试用产品续用…

Thinkpad系列产品进入Bios并设置U盘启动

Thinkpad系列产品&#xff0c;进入Bios并设置U盘启动&#xff0c;常用于以下场景&#xff1a; 1. 安装操作系统。 通过U盘启动盘&#xff0c;用户可以在电脑无法从硬盘启动或需要重装系统时&#xff0c;将操作系统安装到电脑中。这种方法简单且有效&#xff0c;节省了时间并方便…

夏季家里粉尘螨虫满天飞?一招搞定!好用家用空气净化器品牌分享

每到夏季&#xff0c;是家中尘螨滋生的高发期。夏季无论是开窗通风还是关窗开空调&#xff0c;都很容易造成空气中的浮尘堆积&#xff0c;不注意卫生清洁&#xff0c;容易滋生细菌、尘螨。 易过敏、体质弱的人群长时间在空气污染环境中&#xff0c;很容易就会过敏或者发生其他…

成员变量和for循环里面的变量不冲突原因

今天写项目&#xff0c;发现一个类中有一个成员变量与for循环块中的局部变量重名了&#xff0c;但是也没有报错&#xff0c;功能也是正常的&#xff0c;然后了解了一下原因&#xff1a; 成员变量和 for 循环块中的变量不冲突的原因在于它们的作用域&#xff08;Scope&#xff…

2024年最新消防设施操作员(初级)题库

16.LQG10-30表示消防供水管用&#xff0c;设计工作压力为&#xff08; &#xff09;MPa&#xff0c;水带长度为&#xff08; &#xff09;m的轻便消防水龙。 A.10、30 B.10、3.0 C.1.0、30 D.1.0、3.0 答案:C 解析:根据初级教材179页&#xff0c;LQG10-30表示消防供水管用…

Hive笔记-2

第 3 章 DDL (Data Definition Language) 数据定义 DDL数据定义语言 DML数据操作语言 3.1 数据库 (database) 3.1.1 创建数据库 1) 语法 CREATE DATABASE [IF NOT EXISTS] database_name [COMMENT database_comment] [LOCATION hdfs_path] [WITH DBPROPERTIES (property_…

leetcode153. 寻找旋转排序数组中的最小值

leetcode153. 寻找旋转排序数组中的最小值 使用二分法。中间值如果小于等于最右边的值&#xff0c;去左边部分找&#xff1b;中间值如果大于最右边的值&#xff0c;去右半部分找。 def rotate_array_min(nums):n len(nums)low 0high n - 1while low < high:mid low (h…

Git 代码管理规范 !

分支命名 master 分支 master 为主分支&#xff0c;也是用于部署生产环境的分支&#xff0c;需要确保master分支稳定性。master 分支一般由 release 以及 hotfix 分支合并&#xff0c;任何时间都不能直接修改代码。 develop 分支 develop 为开发环境分支&#xff0c;始终保持最…

在微信公众号上怎么添加预定房间功能

在这个快节奏的现代社会&#xff0c;人们对于便捷与高效的需求日益增加。特别是在旅行或出差时&#xff0c;能够快速、方便地预订一间舒适的房间&#xff0c;无疑是每个人心中的小确幸。今天&#xff0c;我们为您带来了一项革命性的服务——微信公众号上的房间预定功能&#xf…

力扣 50.pow(x,n)

class Solution { public: double quickMul(double x,long long N){ if(N0) return 1; double valuequickMul(x,N/2); return N%20?value*value:value*value*x; } double myPow(double x, int n) { long long Nn; return N>0?quickMul(x, N):1.0/quickMul(x, N); } };

Serverless如何赋能餐饮行业数字化?乐凯撒思变之道

导语 | 在数字化浪潮席卷全球的今天&#xff0c;每一个行业都在经历着前所未有的变革。餐饮行业作为人们日常生活中不可或缺的一部分&#xff0c;更是面临着巨大的转型压力。如何完成数字化转型&#xff0c;打破传统经营模式的限制&#xff0c;成为摆在众多餐饮商家面前的一道难…

[二分枚举]特殊密码锁

描述 有一种特殊的二进制密码锁&#xff0c;由n个相连的按钮组成&#xff08;n<30&#xff09;&#xff0c;按钮有凹/凸两种状态&#xff0c;用手按按钮会改变其状态。 然而让人头疼的是&#xff0c;当你按一个按钮时&#xff0c;跟它相邻的两个按钮状态也会反转。当然&am…