数据结构---顺序表

线性表

什么是线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...

线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的(地址不一定是连续的,但是可以通过之间存在的逻辑关系找到下一个数据)线性表在物理上存储时,通常以数组和链式结构的形式存储。

顺序表

什么是顺序表

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

静态顺序表

静态顺序表指的是使用定长数组存储元素的顺序表,由于在使用之前,顺序表的个数就已经确定,对于需要大量增删查改的项目来说很鸡肋,所以静态顺序表只作为顺序表一个小的知识点一带而过。

静态顺序表的初始化

//静态顺序表
typedef int SLDataType;
#define N 10
typedef struct Seqlist
{SLDataType array[N];//定长数组size_t size;//有效数据的个数
}SeqList;

动态顺序表

动态顺序表指的是使用动态开辟的数组存储,顺序表的有效数据个数根据所需要进行修改。

动态顺序表的初始化

//动态顺序表
typedef int SLDateType;
typedef struct SeqList
{SLDateType* array;  //指向动态开辟的数组size_t size;  //数据中存储的数据size_t capacity;   //数组的容量
}SeqList;
  1. 由于动态顺序表不像静态顺序表那样直接给出存储有效数据的个数,所以没有办法直接写出数组,这是就需要指针进行操作,因为指针就是数据的地址,所以要通过定义指针去找到动态开辟的空间,通过指针的加减去对应相应的数据的增删查改。
  2. 动态开辟要有初始条件,只有满足才能扩容,所以加入数组的容量(capacity),当有效数据个数与数组容量相等时,说明顺序表已满,需要扩容。

接口实现

使用数据结构的目的就是为了对数据进行增删查改,那么对于动态顺序表的增删查改,我们使用接口的方式去详细的描述这一过程。

顺序表的初始化

对于顺序表的初始化,主要就是针对顺序表的初始地址,有效数据个数以及最大容量进行设定,只有初始化成功才能开始正常的工作。

//顺序表初始化
void SeqListInit(SeqList* psl) //psl  p指针,sl顺序表
{assert(psl);psl->array = (SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY);if (psl->array == NULL){perror("fail in malloc");return;}psl->size = 0;psl->capacity = INIT_CAPACITY;
}
  1. 起始地址可以置为NULL;但是以上代码是使用malloc函数开辟一块空间,目的是为了使得顺序表的最大容量不为0;当然,如果起始地址是NULL;最大容量就要设置为0;这两种方法都可以。
  2. INIT_CAPACITY是在头文件中定义的大小为4的数据;
  3. 如果对malloc函数和realloc函数不是很清楚,可参考动态内存管理

顺序表的扩容

动态顺序表要时刻检查最大容量是否和有效数据个数相等,如果相等就说明要进行扩容操作

//检查空间,如果满了,进行增容
void SeqListCheckCapacity(SeqList* psl)
{assert(psl);if (psl->size == psl->capacity){SLDataType* newnode = (SLDataType*)realloc(psl->array, sizeof(SLDataType)* 2 * psl->capacity);if (newnode == NULL){perror("fail in realloc");return;}psl->array = newnode;psl->capacity *= 2;}
}
  1. realloc函数分为原地扩容或者异地扩容,这两种扩容方式是根据电脑不同时刻内存的情况选择的,处理扩容问题最正确的办法就是先设定一个新的地址去保存扩容出来的内存,再将新的地址赋值给原来的地址。
  2. 我们要扩容到最大容量的2倍,很多同学会写成   sizeof(SLDataType)* 2   ,这个是4*2,8个字节的大小,但是最大容量是4*4字节,也就是16字节,2倍就是32个字节;所以这么写完就会报错,报错的原因就是扩容扩少了,导致新的元素没有地方存放,所以正确的写法是 sizeof(SLDataType)* 2 * psl->capacity
  3. 扩容成功之后,除了地址要改变以外,顺序表的最大容量也要*2,这样才是正确的扩容。

顺序表的打印

打印是一个很好检查自己代码错误以及输出的方法,一定要先写打印,然后用其测试头插头删,尾插尾删。

//打印顺序表
void PrintSL(SeqList* psl)
{assert(psl);for (int i = 0; i < psl->size; i++){printf("%d ", psl->array[i]);}printf("\n ");
}
  1. assert断言函数是为了防止顺序表为空而加入的,各位同学在写的时候一定要注意,仔细思考一下顺序表为空能不能打印,能不能进行增删查改,答案肯定是不能,顺序表都已经空了,你打印什么,增删查改谁?
  2. 还有就是size-1才是array数组的最后一个下标

顺序表的尾插

从图中就可以明白,尾插其实就是赋值给array[size],前提是要检查一下存储空间够不够用

同时尾插之后要注意,数组存储的有效数据个数已经增加了

//顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x)
{assert(psl);SeqListCheckCapacity(psl);psl->array[psl->size] = x;//size是顺序表的最后一个位置psl->size++;
}

顺序表的尾删

对于顺序表的删除,一定要注意,要保证有效数据个数,也就是size>0;如果没有数据你删什么。

从图中可以看出来,顺序表的尾删是什么,就是把尾巴的数据直接扔掉就行了。

//顺序表尾删
void SeqListPopBack(SeqList* psl)
{assert(psl);assert(psl->size > 0);psl->size--;
}

 很多小伙伴会质疑,realloc函数开辟出来的空间,不用的时候应该使用free函数将空间的使用权还给操作系统啊,但是请注意,动态内存函数开辟出来的空间,只能一起还给操作系统,不能一个一个free,我们可以在最后使用free函数将所有开辟出来的空间全部还给操作系统。

顺序表的头插

//顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x)
{assert(psl);SeqListCheckCapacity(psl);for (int i = psl->size; i > 0; i--){psl->array[i] = psl->array[i - 1];}psl->array[0] = x;psl->size++;
}

 

顺序表的头删

//顺序表头删
void SeqListPopFront(SeqList* psl)
{assert(psl);assert(psl->size > 0);for (int i = 0; i < psl->size-1; i++){psl->array[i] = psl->array[i + 1];}psl->size--;
}

 顺序表的查找

int SeqListFind(SeqList* psl, SLDataType x)
{assert(psl);for (int i = 0; i < psl->size; i++){if (psl->array[i] == x){return i;}}return -1;
}

顺序表在pos位置插入x

pos指的是数组的下标,在寻找的时候注意头和尾,pos=size 相当于尾插,

//顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{assert(psl);assert(pos>=0 && pos <= psl->size);SeqListCheckCapacity(psl);for (int i = psl->size; i > pos; i--){psl->array[i] = psl->array[i - 1];}psl->array[pos] = x;psl->size++;
}

上述代码可以规避pos=0的情况,如果pos=0;i可以在0的位置停下来。相当于是头插; 如果是这样的代码会出现什么情况呢


void SeqListInsert(SeqList* psl, size_t pos, SLDateType x)
{assert(psl);assert(pos<=psl->size);for (int i = psl->size-1; i >= pos; i--){psl->array[i + 1] = psl->array[i];}}

当在下标为0的位置上插入一个数据时,i从psl->size-1到0,i进行i--操作,此时i=-1,再执行i>=pos操作,此时会停止循环吗?答案是不会,大家可以去调试,确实不会让循环停下。

那为什么呢?因为pos为 size_t 的类型,size_t 为无符号整形-,当int(有符号整形)和size_t(无符号整形)比较大小时,int型的数据会发生算数转换,转换成unsigned int型,此时为负数的 i 就变成了很大的数字,自然而然比0大,因此会进入死循环。


顺序表删除pos位置的值

删除pos位置的值其实就是另一种的头删,采用覆盖pos位置的值的方法

void SeqListErase(SeqList* psl, size_t pos)
{assert(psl);assert(pos >= 0 && pos < psl->size);for (int i = pos; i < psl->size - 1; i++){psl->array[i] = psl->array[i + 1];}psl->size--;
}

顺序表的销毁

void SeqListDestory(SeqList* psl)
{assert(psl);free(psl->array);psl->array = NULL;psl->capacity = psl->size = 0;
}

总代码

头文件--seqlist.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#define INIT_CAPACITY 4
typedef int SLDataType;
typedef struct Seqlist
{SLDataType* array;size_t size;size_t capacity;
}SeqList;//顺序表初始化
void SeqListInit(SeqList* psl);  //psl  p指针,sl顺序表
//检查空间,如果满了,进行增容
void SeqListCheckCapacity(SeqList* psl);
//顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x);
//顺序表尾删
void SeqListPopBack(SeqList* psl);
//顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x);
//顺序表头删
void SeqListPopFront(SeqList* psl);
//顺序表查找
int SeqListFind(SeqList* psl, SLDataType x);
//顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
//顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos);
//顺序表销毁
void SeqListDestory(SeqList* psl);
//打印顺序表
void PrintSL(SeqList* psl);

函数文件--seqlist.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"seqlist.h"//打印顺序表
void PrintSL(SeqList* psl)
{assert(psl);for (int i = 0; i < psl->size; i++){printf("%d ", psl->array[i]);}printf("\n ");
}//顺序表初始化
void SeqListInit(SeqList* psl) //psl  p指针,sl顺序表
{assert(psl);psl->array = (SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY);if (psl->array == NULL){perror("fail in malloc");return;}psl->size = 0;psl->capacity = INIT_CAPACITY;
}//检查空间,如果满了,进行增容
void SeqListCheckCapacity(SeqList* psl)
{assert(psl);if (psl->size == psl->capacity){SLDataType* newnode = (SLDataType*)realloc(psl->array, sizeof(SLDataType)* 2 * psl->capacity);if (newnode == NULL){perror("fail in realloc");return;}psl->array = newnode;psl->capacity *= 2;}
}//顺序表销毁
void SeqListDestory(SeqList* psl)
{assert(psl);free(psl->array);psl->array = NULL;psl->capacity = psl->size = 0;
}//顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x)
{assert(psl);SeqListCheckCapacity(psl);psl->array[psl->size] = x;//size是顺序表的最后一个位置psl->size++;
}//顺序表尾删
void SeqListPopBack(SeqList* psl)
{assert(psl);assert(psl->size > 0);psl->size--;//不能单独free,申请的空间要不然就一起释放掉,不能单独释放某一段空间
}//顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x)
{assert(psl);SeqListCheckCapacity(psl);for (int i = psl->size; i > 0; i--){psl->array[i] = psl->array[i - 1];}psl->array[0] = x;psl->size++;
}
//顺序表头删
void SeqListPopFront(SeqList* psl)
{assert(psl);assert(psl->size > 0);for (int i = 0; i < psl->size-1; i++){psl->array[i] = psl->array[i + 1];}psl->size--;
}
//顺序表查找
int SeqListFind(SeqList* psl, SLDataType x)
{assert(psl);for (int i = 0; i < psl->size; i++){if (psl->array[i] == x){return i;}}return -1;
}
//顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{assert(psl);assert(pos>=0 && pos <= psl->size);SeqListCheckCapacity(psl);for (int i = psl->size; i > pos; i--){psl->array[i] = psl->array[i - 1];}psl->array[pos] = x;psl->size++;
}
//顺序表删除pos位置的值void SeqListErase(SeqList* psl, size_t pos)
{assert(psl);assert(pos >= 0 && pos < psl->size);for (int i = pos; i < psl->size - 1; i++){psl->array[i] = psl->array[i + 1];}psl->size--;
}

测试文件--test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"seqlist.h"void Test()
{SeqList seq;SeqListInit(&seq);SeqListPushBack(&seq, 1);SeqListPushBack(&seq, 1);SeqListPushBack(&seq, 1);SeqListPushBack(&seq, 1);SeqListPushBack(&seq, 1);SeqListPushBack(&seq, 1);SeqListPushBack(&seq, 1);SeqListPushFront(&seq, 4);SeqListPopFront(&seq);SeqListInsert(&seq, 3, 6);SeqListInsert(&seq, 3, 6);SeqListInsert(&seq, 3, 6);SeqListInsert(&seq, 3, 6);SeqListInsert(&seq, 3, 6);SeqListInsert(&seq, 3, 6);SeqListErase(&seq, 3);PrintSL(&seq);SeqListDestory(&seq);
}int main()
{Test();return 0;
}

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

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

相关文章

树莓派基金会近日发布了新版基于 Debian 的树莓派操作系统

导读树莓派基金会&#xff08;Raspberry Pi Foundation&#xff09;近日发布了新版基于 Debian 的树莓派操作系统&#xff08;Raspberry Pi OS&#xff09;&#xff0c;为树莓派单板电脑带来了新的书虫基础和一些重大变化。 新版 Raspberry Pi OS 的最大变化是它现在基于最新的…

Spring概述

Spring概述 Spring 是最受欢迎的企业级 Java 应用程序开发框架&#xff0c;数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。 Spring 框架是一个开源的 Java 平台&#xff0c;它最初是由 Rod Johnson 编写的&#xff0c;并且于 2003 …

0基础学习VR全景平台篇第114篇:全景图优化和输出 - PTGui Pro教程

上课&#xff01;全体起立~ 大家好&#xff0c;欢迎观看蛙色官方系列全景摄影课程&#xff01; 前情回顾&#xff1a;之前&#xff0c;我们详细介绍了如何用编辑器、控制点、垂直线等功能优化错位和矫正水平&#xff0c;然而这些调整不会马上生效。 我们需要在【优化】选项卡…

Dart HttpClient 网络请示框架的使用详解

Dart的HttpClient库是一个用于发送HTTP请求的库&#xff0c;它提供了一个简单的API来执行HTTP请求和接收响应。下面是一个详细的HttpClient使用指南。 1. 导入HttpClient库 首先&#xff0c;确保你已经将HttpClient库导入到你的Dart项目中。你可以使用pubspec.yaml文件中的de…

我是如何走上测试管理岗的

最近有小伙伴问了一个问题&#xff1a;他所在的测试团队规模比较大&#xff0c;有 50 多个人&#xff0c;分成了 4 ~ 5 个小组。这位同学觉得自己的技术能力在团队里应该属于比较不错的&#xff0c;但疑惑的是在几次组织架构调整中&#xff0c;直属领导一直没有让他来管理一个小…

新的iLeakage攻击从Apple Safari窃取电子邮件和密码

图片 导语&#xff1a;学术研究人员开发出一种新的推测性侧信道攻击&#xff0c;名为iLeakage&#xff0c;可在所有最新的Apple设备上运行&#xff0c;并从Safari浏览器中提取敏感信息。 攻击概述 iLeakage是一种新型的推测性执行攻击&#xff0c;针对的是Apple Silicon CPU和…

一、高效构建Java应用:Maven入门和进阶

一、高效构建Java应用&#xff1a;Maven入门和进阶 目录 一、Maven简介和快速入门 1.1 Maven介绍1.2 Maven主要作用理解1.3 Maven安装和配置 二、基于IDEA的Maven工程创建 2.1梳理Maven工程GAVP属性2.2 Idea构建Maven JavaSE工程2.3 Idea构建Maven JavaEE工程2.4 Maven工程项…

C++进阶语法——OOP(面向对象)【学习笔记(四)】

文章目录 1、C OOP⾯向对象开发1.1 类&#xff08;classes&#xff09;和对象&#xff08;objects&#xff09;1.2 public、private、protected访问权限1.3 实现成员⽅法1.4 构造函数&#xff08;constructor&#xff09;和 析构函数&#xff08;destructor&#xff09;1.4.1 构…

ubuntu 22.04安装百度网盘

百度网盘 客户端下载 (baidu.com) 下载地址 sudo dpkg -i baidunetdisk_4.17.7_amd64.deb

tftp服务的搭建

TFTP服务的搭建 1 先更新一下apt包 sudo apt-get update2 服务器端(虚拟机上)安装 TFTP相关软件 sudo apt-get install xinetd tftp tftpd -y3 创建TFTP共享目录 mkdir tftp_sharetftp_shaer的路径是/home/cwz/tftp_share 3.1 修改共享目录的权限 sudo chmod -R 777 tftp…

北邮22级信通院数电:Verilog-FPGA(7)第七周实验(1):带使能端的38译码器全加器(关注我的uu们加群咯~)

北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 关注作者的uu们可以进群啦~ 目录 方法一&#xff…

Python第三方库 - Flask(python web框架)

1 Flask 1.1 认识Flask Web Application Framework&#xff08; Web 应用程序框架&#xff09;或简单的 Web Framework&#xff08; Web 框架&#xff09;表示一个库和模块的集合&#xff0c;使 Web 应用程序开发人员能够编写应用程序&#xff0c;而不必担心协议&#xff0c;线…