【数据结构】——顺序表介绍(独家介绍,小白必看!!)

2023-10-30

重点和易错点都用彩笔标记出来了,放心食用!!

数据结构分为线性表非线性表,今天我们要学习的顺序表就是线性表中的一个小类。那么,何为线性表,线性表是指n个具有相同性质的数据元素的有限序列,常见的线性表有:顺序表、链表、栈、队列、字符串等等。注意,线性表的物理结构不一定是线性的,它在逻辑结构上一定是线性的(这个很好理解,等我们学完顺序表和单链表这对黄金搭档,就明白这句话的含义了)

今天我们重点讲解顺序表,顺序表是线性表,顺序表在逻辑结构和物理结构上都是线性的


 1、概念及结构

顺序表(SeqList):顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构(连续存储数据,不能跳跃)。

一般我们用数组存储顺序表,在数组上完成数据的增删查改。

顺序表分为两种类型:

//静态顺序表
#define N 7
typedef int SLDateType;
typedef struct SeqList
{
	SLDateType array[N];  //定长数组
	size_t size;                  //有效数据长度,size_t是无符号整形
}SeqList;

//动态顺序表
typedef int SLDateType;
typedef struct SeqList
{
	SLDateType* array;  //指向动态开辟的数组
	size_t size;  //数据中存储的数据
	size_t capacity;   //数组的容量
}SeqList;

 我建议用动态顺序表,比起静态顺序表,动态的更加好调整顺序表的大小。接下来,我也会以动态顺序表为例,介绍如何实现动态顺序表的增删查改。


2、接口实现

2.1 功能要求

我们要实现以下功能

//顺序表初始化
void SeqListInit(SeqList* psl);  //psl  p指针,sl顺序表
//检查空间,如果满了,进行增容
void SeqListCheckCapacity(SeqList* psl);
//顺序表尾插
void SeqListPushBack(SeqList* psl,SLDateType x);
//顺序表尾删
void SeqListPopBack(SeqList* psl);
//顺序表头插
void SeqListPushFront(SeqList* psl, SLDateType x);
//顺序表头删
void SeqListPopFront(SeqList* psl);
//顺序表查找
int SeqListFind(SeqList* psl, SLDateType x);
//顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos,SLDateType x);
//顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos);
//顺序表销毁
void SeqListDestory(SeqList* psl);
//打印顺序表
void PrintSL(SeqList* psl);
//修改顺序表
void SLModify(SeqList* psl, size_t pos, SLDateType x);

2.2 功能实现

2.2.1 打印顺序表

这里提一嘴,我建议在每写一个功能就测试一下,千万不要把大部分功能写完再统一测试,那样你的Bug可能会99+(别问我为什么知道)。

//打印顺序表
void SeqListPrint(SeqList* psl)
{
	assert(psl);
	for(int i=0;i<psl->size;i++)
	{
		printf("%d ", psl->array[i]);
	}
	printf('\n');
}

2.2.2 顺序表初始化

链表初始化没什么好说的,我直接上代码了。

void SeqListInit(SeqList*psl)
{
  assert(psl);   //断言psl是不是空指针
  psl->array=NULL;
  psl->size=psl->capacity=0;
}

2.2.3 检查顺序表的空间,增容

思路讲解:

  1. 判断顺序表空间是否满了,也就是判断是否需要增容就是要看psl->size==psl->capacity?增容:不增容。
  2. 用条件运算符来确定增容后的新空间的大小,如果是仍未存储数据的新链表,则先让链表的容量为4(这个数值可以随便设置);如果已经存储了数据,但容量不够了,我们就让链表的空间每次增加一倍,也就是变成原来的两倍(可能有人会疑惑,为什么是两倍,其实这个也是为了减少数据的浪费,变成2倍比较保守)。
  3. 在扩容时用realloc,当realloc的第一个参数为0时,其效果等同于malloc用realloc可以完美实现最初开辟空间和增容的功能
  4. 检查tmp是否为空,也就是检查是否成功开辟了新的空间,非空则把tmp赋值给psl->array最后千万不要忘记更改capacity的值
//检查容量,增容
void CheckCapacity(SeqList* psl)
{
	assert(psl);
	if (psl->size == psl->capacity)
	{
		size_t newcapacity = (psl->capacity == 0 ? 4 : psl->capacity * 2);
		//这里用realloc非常好
		SLDateType* tmp = (SLDateType*)realloc(psl->array, psl->capacity * sizeof(SLDateType));
		if (tmp == NULL)
		{
			perror("realloc:");
			return;
		}
		psl->array = tmp;
		psl->capacity = newcapacity;
	}
}

2.2.4 顺序表尾插

这里就需要注意在尾插前检查容量,并且不要忘记psl->size++

//顺序表尾插
void SeqListPushBack(SeqList* psl, SLDateType x)
{
	assert(psl);
	CheckCapacity(psl);
	psl->array[psl->size] = x;
	//别忘记让psl->size+1
	psl->size++;
}

2.2.5 顺序表尾删

注意:不管是进行头删还是进行尾删,我们都要检查psl->size是否大于0,我也提供了两种检查的方式,选其一即可。

//顺序表尾删
void SeqListPopBack(SeqList* psl)
{
	assert(psl);
	//千万不要忘记检查psl->size是否大于0
	//检查方式一:温柔的检查
	/*if (psl->size == 0)
		return;*/
	//检查方式二:暴力的检查
	assert(psl->size>0);
	psl->size--;
}

2.2.6 顺序表头插

这个也没什么好说的,直接上代码。

void SeqListPushFront(SeqList*psl, SLDateType x)
{
	assert(psl);
	CheckCapacity(psl);
	for (int i = psl->size;i>0; i++)
	{
		psl->array[i] = psl->array[i - 1];
	}
	psl->array[0] = x;
	psl->size++;
}

2.2.7 顺序表头删

删除数据不要忘记暴力检查psl->size。

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

2.2.8 顺序表查找

找到了,返回值为数组下标;未找到,返回值为-1。

//顺序表查找(返回下标,找不到就返回-1)
int SeqListFind(SeqList* psl, SLDateType x)
{
	assert(psl);
	for (int i = 0; i < psl->size; i++)
	{
		if (psl->array[i] == x)
		{
			return i;
		}
	}
	return -1;
}

2.2.9 在pos位置插入值

这个功能在实现的过程中一不小心就出问题,注意注意!!!!

//顺序表在pos位置插入x(pos为下标值)
void SeqListInsert(SeqList* psl, size_t pos, SLDateType x)
{
	assert(psl);
	assert(pos<=psl->size);//注意:pos是可以等于psl->size,此时就相当于尾插
	CheckCapacity(psl);
    //写法一:
	for (int i = psl->size-1; i >= pos; i--)
	{
		psl->array[i + 1] = psl->array[i];
	}
    //写法二:
    size_t end=psl->size-1;
    while(end)
    {
        psl->array[end+1]=psl->array[end];
        end--;
    }
	psl->array[pos] = x;
	psl->size++;
}

当在下标为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大,因此会进入死循环。

那怎么解决呢?

针对写法一的解决办法:

for (int i = psl->size; i > pos; i--)
{
	psl->array[i] = psl->array[i-1];
}

此时,避免了i==0的情况,保证i始终大于0,这样i--就不会出现问题了。

针对写法二的解决方法:

size_t end=psl->size;
while(end)
{
    psl->array[end]=psl->array[end-1];
    end--;
}

有人肯定会不理解,为什么非让pos为size_t类型,如果让pos变成int型,只需要保证pos大于等于0就可以了,size_t显得好像更麻烦一些。其实,我们把pos写成size_t是因为库也是这样写的,我们严格根据库的声明来实现,以后当我们涉及到的问题更加复杂时,用size_t做接口肯定比int更好。

2.2.10 顺序表删除pos的位置

这里注意好循环时i的上下限就可以了。

//顺序表删除pos的位置
void SeqListErase(SeqList*psl, size_t pos)
{
	assert(psl);
	assert(pos < psl->size);
	for (int i = pos;i<psl->size-1;i++)
	{
		psl->array[i] = psl->array[i + 1];
	}
	psl->size--;
}

2.2.11 修改pos位置的值

//修改顺序表的值
void SeqListModify(SeqList*psl, size_t pos,SLDateType x)
{
	assert(psl);
	assert(pos < psl->size);
	psl->array[pos] = x;
}

2.2.12 销毁顺序表

//销毁顺序表
void SeqListDestory(SeqList*psl)
{
	assert(psl);
	free(psl->array);
	psl->array = NULL;
	psl->size = psl->capacity = 0;
}

3、总代码

SeqList.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDateType;
静态版本
//#define N 7
//typedef struct SeqList
//{
//	SLDateType array[N];   //静态数组
//	size_t size;   //有效数据个数
//}SeqList;
//动态版本
typedef struct SeqList
{
	SLDateType* array;   //指向动态开辟的数组
	size_t size;   //有效数据个数
	size_t capacity;  //容量空间的大小
}SeqList;

//基本增删查改接口
//顺序表初始化
void SeqListInit(SeqList* psl);  //psl  p指针,sl顺序表
//检查空间,如果满了,进行增容
void CheckCapacity(SeqList* psl);
//顺序表尾插
void SeqListPushBack(SeqList* psl,SLDateType x);
//顺序表尾删
void SeqListPopBack(SeqList* psl);
//顺序表头插
void SeqListPushFront(SeqList* psl, SLDateType x);
//顺序表头删
void SeqListPopFront(SeqList* psl);
//顺序表查找
int SeqListFind(SeqList* psl, SLDateType x);
//顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos,SLDateType x);
//顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos);
//顺序表销毁
void SeqListDestory(SeqList* psl);
//打印顺序表
void PrintSL(SeqList* psl);
//修改顺序表
void SLModify(SeqList* psl, size_t pos, SLDateType x);

SeqList.c

#include"SeqList.h"
//打印顺序表
void PrintSL(const SeqList* psl)
{
	assert(psl);
	for (int i = 0; i < psl->size; i++)
	{
		printf("%d ", psl->array[i]);
	}
	printf("\n");
}
//初始化顺序表
void SeqListInit(SeqList* psl)
{
	assert(psl);
	psl->array = NULL;
	psl->size = psl->capacity = 0;
}
//检查容量,增容
void CheckCapacity(SeqList* psl)
{
	assert(psl);
	if (psl->size == psl->capacity)
	{
		size_t newcapacity = (psl->capacity == 0 ? 4 : psl->capacity * 2);
		//这里用realloc非常好,当realloc的第一个参数为0时,其效果等同于malloc
		//用realloc可以完美实现最初开辟空间和增容的功能

		//一定要注意:是newcapacity,而不是psl->capacity
		SLDateType* tmp = (SLDateType*)realloc(psl->array, newcapacity * sizeof(SLDateType));
		if (tmp == NULL)
		{
			perror("realloc:");
			return;
		}
		psl->array = tmp;
		psl->capacity = newcapacity;
	}
}
//顺序表尾插
void SeqListPushBack(SeqList* psl, SLDateType x)
{
	assert(psl);
	CheckCapacity(psl);
	psl->array[psl->size] = x;
	//也可以用SeqListInsert(psl,psl->size,x),但经常用上面的方法,可读性更强
	//可别忘了让psl->size加1
	psl->size++;
}

//顺序表头插
void SeqListPushFront(SeqList* psl, SLDateType x)
{
	assert(psl);
	CheckCapacity(psl);
	for (int i = psl->size; i > 0; i--)
	{
		psl->array[i] = psl->array[i - 1];
	}
	psl->array[0] = x;
	//也可以用SeqListInsert(psl,0,x),但经常用上面的方法,可读性更强
	psl->size++;
}

//顺序表尾删
void SeqListPopBack(SeqList* psl)
{
	assert(psl);
	//千万不要忘记检查size是否大于0
	//温柔地检查SL中的size是否大于0
	/*if (psl->size == 0)
	{
		return;
	}*/
	//暴力的检查
	assert(psl->size > 0);
	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, SLDateType x)
{
	assert(psl);
	for (int i = 0; i < psl->size; i++)
	{
		if (psl->array[i] == x)
		{
			return i;
		}
	}
	return -1;
}

//顺序表在pos位置插入x(pos指的是下标)
void SeqListInsert(SeqList* psl,size_t pos,SLDateType x)
{
	assert(psl);
	CheckCapacity(psl);
	assert(pos <= psl->size);//size_t是无符号整形,所以不需要担心pos小于0
	//而pos等于psl->size,此时就不是插在中间了,而是尾插了。
	//易错:pos=0
	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<psl->size);
	for (int i = pos; i < psl->size; 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;
}
//修改顺序表
void SLModify(SeqList* psl, size_t pos, SLDateType x)
{
	assert(psl);
	assert(psl < psl->size);
	psl->array[pos] = x;
}

test.c

test.c可以自己写,记得引用头文件就可以。

#include"SeqList.h"
int main()
{
	SeqList SL;
	SeqListInit(&SL);
	SeqListPushBack(&SL, 5);
	SeqListPushBack(&SL, 2);
	SeqListPushFront(&SL, 0);
	PrintSL(&SL);
	SeqListInsert(&SL, 2, 3);
	PrintSL(&SL);
	SeqListInsert(&SL, 2, 3);
	PrintSL(&SL);
	return 0;
}

这是数据结构第二篇文章了,数据结构有些小难♂,但别担心,快关注我跟我一起坚持学习吧!(*^▽^*)

如果喜欢我的文章就给我点个赞再走吧!下次见!拜拜 ┏(^0^)┛

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【数据结构】——顺序表介绍(独家介绍,小白必看!!) 的相关文章

随机推荐

  • webpack中loader加载器的使用及原理(常用的loader加载器)

    webpack的loaders是一块很重要的组成部分 我们都知道webpack是用于资源打包的 里面的所有资源都是 模块 内部实现了对模块资源进行加载的机制 但是Webpack本身只能处理 js模块 如果要处理其他类型的文件 就需要使用 l
  • 电信光纤天翼网关将默认的路由模式修改为桥接模式

    前两年将家里的电信宽带升级到光纤 光猫也随之进行了升级 当时升级好后 电信工作人员介绍说新的光猫带有wifi功能 如果连接路由器可以不用配置路由器的拨号设置 说是升级到光纤后可以直接连接网线上网 不用再拨号了 当时也没怎么在意 网线连上路由
  • 基于 SpringBoot 的 4S店车辆管理系统,可作为毕业设计

    博主介绍 程序员徐师兄 7年大厂程序员经历 全网粉丝30W Csdn博客专家 掘金 华为云 阿里云 InfoQ等平台优质作者 专注于Java技术领域和毕业项目实战 文章目录 1 简介 2 技术栈 3 功能总览 4 系统设计 4 1 系统设计
  • 1023 组最小数

    题目 给定数字 0 9 各若干个 你可以以任意顺序排列这些数字 但必须全部使用 目标是使得最后得到的数尽可能小 注意 0 不能做首位 例如 给定两个 0 两个 1 三个 5 一个 8 我们得到的最小的数就是 10015558 现给定数字 请
  • CURL解析超时的解决方案

    背景 项目中需要在抓取纷享销客CRM图片上传到OSS 调用OssClient php时 容易发生解析超时 多重试几次就ok 错误提示 2019 04 08 19 41 01 lumen DEBUG 出错文件 home zrj www adm
  • elementui——el-form动态表单props正确写法,如何使用 validateField

    el form动态表单props正确写法 如何使用 validateField
  • Pywinauto+某应用程序(学习至第9讲)--受阻

    文章目录 Pywinauto 某应用程序 学习至第9讲 受阻 问题点 1 安装第三方库 2 自动化的切入点 后端技术 程序个数 3 程序辅助检查工具的使用 inspect spy 4 pywinauto打开指定的应用程序 XXX exe 5
  • Matroska文件的SRT Subtitle

    1 SRT简单介绍 SRT是一种比较流行的文本字幕 因为是文本格式 所以就比较小了 因为其制作规范简单 一句时间代码 一句字幕 使得制作修改就相当简单 配合上 style文件还能让srt自带一些字体上的特效等 SRT文件中的字幕包括四个部分
  • 批量调整word 图片大小

    打开文档后 按Alt F11 在左边Porject下找到ThisDocument 右键插入模块 贴上下面的Sub Macro For Each iShape In ActiveDocument InlineShapesiShape Heig
  • 【安卓学习之常见问题】文件分享--文件不存在

    安卓学习之常见问题 文件分享 文件不存在 系列文章目录 提示 这里是收集了和文件分享有关的文章 安卓学习之常见问题 android路径及文件问题 安卓学习之常见问题 文件分享 文件不存在 文章目录 安卓学习之常见问题 文件分享 文件不存在
  • LabVIEW深度相机与三维定位实战(下)

    博客主页 virobotics的CSDN博客 LabVIEW深度学习 人工智能博主 所属专栏 LabVIEW深度学习实战 上期文章 LabVIEW深度相机与三维定位实战 上 如觉得博主文章写的不错或对你有所帮助的话 还望大家多多支持呀 欢迎
  • 什么是内存泄漏,一看就懂,一学就会!!大白话解释内存泄漏!通俗易懂!

    在 32 位环境下 一个程序占用 4GB 的内存 其中 内核空间 是被操作系统占用的 我们没法直接干预 保留区域 也不用来存储数据 只用作一些特殊目的 比如 你可以让空指针指向这里 除了这两个区域 剩下的那些内存才是被我们自己编写的程序所占
  • Oracle数据库常见版本

    在Oracle数据库的发展中 数据库一直处于不断升级状态 有以下几个版本 Oracle 8 Oracle 8i Oracle 8i表示Oracle正式向Internet上开始发展 其中i表示就是internet Oracle 9i Orac
  • 带你玩转Spring Cloud Tencent(一)概述

    项目地址 spring cloud tencent 介绍 Spring Cloud Tencent 是腾讯开源的一站式微服务解决方案 Spring Cloud Tencent 实现了Spring Cloud 标准微服务 SPI 开发者可以基
  • PHP密码复杂性验证,JS检查密码强度 检查密码复杂度

    pass keyup function e var strongRegex new RegExp 8 A Z a z 0 9 W g var mediumRegex new RegExp 7 A Z a z A Z 0 9 a z 0 9
  • 电信光猫天翼网关usb插U盘共享文件

    ftp用不了 samba可以用 1 在电脑文件管理器中输入 192 168 1 1打开 在弹出框中输入光猫背后的账号密码登录即可打开共享的U盘 2 在手机ES文件管理器中 点右上角三点 新建 在弹出框中填入192 168 1 1和选择sam
  • Nginx 官网及中文官网

    英语官方 http nginx org 中文文档 http www nginx cn doc 转载于 https blog 51cto com hacker3389 1877270
  • 什么是大数据(转自知乎)

    声明 纯属个人收藏用 什么是大数据 大数据只是一个空洞的商业术语 就跟所谓的商业智能一样空洞无物 当然 这并不是说大数据没有意义 只是对于不同的人有不同的含义 A 对于投资人和创业者而言 大数据是个热门的融资标签 就和前几年流行的 SoLo
  • 磁盘快照技术

    一 概念解释 像照相机一样 机器快门一闪 很快就把刚刚的人像停留在了相纸上 存储系统中的数据 快照 与我们生活中所说的 照片 非常相似 所不同的是 照片的对象不是人 而是数据 如同照片留住了我们过去的摸样和岁月 快照把数据在某一时刻的映像也
  • 【数据结构】——顺序表介绍(独家介绍,小白必看!!)

    重点和易错点都用彩笔标记出来了 放心食用 数据结构分为线性表和非线性表 今天我们要学习的顺序表就是线性表中的一个小类 那么 何为线性表 线性表是指n个具有相同性质的数据元素的有限序列 常见的线性表有 顺序表 链表 栈 队列 字符串等等 注意