文章目录

  • (1)线性表
  • (2)顺序表
    • 1)什么是顺序表
    • 2)顺序表的定义
    • 2)顺序表的接口实现
      • 1、初始化顺序表
      • 2、销毁(释放)顺序表
      • 3、检查顺序表容量是否满了,好进行增容
      • 3、顺序表尾插
      • 4、顺序表尾删
      • 5、顺序表头插
      • 6、顺序表头删
      • 7、打印顺序表
      • 8、在顺序表中查找指定值
      • 9、在顺序表指定下标位置插入数据(要注意下int与size_t间的转换问题)
      • 10、在顺序表中删除指定下标位置的数据
      • 11、查看顺序表中有效数据个数
      • 12、修改指定下标位置的数据

(1)线性表

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

  • 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。


(2)顺序表

1)什么是顺序表

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

  • 顺序表:可动态增长的数组,要求数据是连续存储的

2)顺序表的定义

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

  • 缺陷:给小了不够用,给大了可能浪费,非常不实用
#define N 10
typedef int SLDataType;typedef struct SeqList
{SLDataType array[N];  //定长数组size_t size;          //有效数据个数
}SeqList;

2、动态顺序表:使用动态开辟的数组存储元素

  • 动态顺序表可根据我们的需要分配空间大小
  • size 表示当前顺序表中已存放的数据个数
  • capacity 表示顺序表总共能够存放的数据个数
typedef int SLDataType; //类型重命名,后续要存储其它类型时方便更改typedef struct SeqList
{SLDataType* a;    //指向动态开辟的数组size_t size;      //有效数据个数size_t capacity;  //容量大小
}SeqList;

2)顺序表的接口实现

首先新建一个工程( 博主使用的是 VS2019 )此次用的是动态顺序表

  • SeqList.h(顺序表的类型定义、接口函数声明、引用的头文件)
  • SeqList.c(顺序表接口函数的实现)
  • Test.c(主函数、测试顺序表各个接口功能)

如图:

  • SeqList.h 头文件代码如下:
#pragma once  //防止头文件被二次引用#include<stdio.h>   /*perror, printf*/
#include<assert.h>  /*assert*/
#include<stdlib.h>  /*realloc*/typedef int SLDataType;  //后续要存储其它类型时方便更改
//顺序表的动态存储
typedef struct SeqList
{SLDataType* a;    //指向动态开辟的数组size_t size;      //有效数据个数size_t capacity;  //容量大小
}SeqList;//初始化顺序表
void SeqListInit(SeqList* psl);
//销毁顺序表
void SeqListDestory(SeqList* psl);
//检查顺序表容量是否满了,好进行增容
void CheckCapacity(SeqList* psl);
//顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x);//O(1)
//顺序表尾删
void SeqListPopBack(SeqList* psl);//O(1)
//顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x);//O(n)
//顺序表头删
void SeqListPopFront(SeqList* psl);//O(n)
//打印顺序表
void SeqListPrint(const SeqList* psl);
//在顺序表中查找指定值
int SeqListFind(const SeqList* psl, SLDataType x);
//在顺序表指定下标位置插入数据
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
//在顺序表中删除指定下标位置的数据
void SeqListErase(SeqList* psl, size_t pos);
//查看顺序表中数据个数
size_t SeqListSize(const SeqList* psl);
//修改指定下标位置的数据
void SeqListAt(SeqList* psl, size_t pos, SLDataType x);

这里重点讲解 SeqList.c 中各个接口函数的实现

1、初始化顺序表

记得一定要加上断言,防止传进来的指针为空

void SeqListInit(SeqList* psl)
{assert(psl != NULL);  //断言psl->a = NULL;  //初始顺序表为空psl->size = 0;  //初始数据个数为0psl->capacity = 0;  //初始空间容量为0
}

2、销毁(释放)顺序表

记得一定要加上断言,防止传进来的指针为空

void SeqListDestory(SeqList* psl)
{assert(psl != NULL);  //断言free(psl->a);   //释放动态开辟的空间psl->a = NULL;  //置空psl->size = 0;  //数据个数置0psl->capacity = 0;  //空间容量大小置0
}

3、检查顺序表容量是否满了,好进行增容

为什么不采取插一个数据,增容一个空间的方式呢,因为这样也太麻烦了,代价也太大了,一般情况下,为了避免频繁的增容,当空间满了后,我们不会一个一个的去增,而是一次增容 2 倍,当然也不会一次增容太大,比如 3 倍 4 倍,空间可能会浪费,2 倍是一个折中的选择。

void CheckCapacity(SeqList* psl)
{assert(psl != NULL);  //断言if (psl->size == psl->capacity)  //检查容量,满了则增容{size_t newcapacity;  //新容量if (psl->capacity == 0)newcapacity = psl->capacity = 4;  //原来容量为0,扩容为4elsenewcapacity = 2 * psl->capacity;  //原来容量不为0,扩容为原来的2倍SLDataType* p = (SLDataType*)realloc(psl->a, newcapacity*sizeof(SLDataType));  //扩容if (p == NULL){perror("realloc");exit(-1);}psl->a = p;  // p 不为空,开辟成功psl->capacity = newcapacity;  //更新容量}
}

3、顺序表尾插

void SeqListPushBack(SeqList* psl, SLDataType x)
{assert(psl != NULL);  //断言CheckCapacity(psl);  //检查顺序表容量是否已满psl->a[psl->size] = x;  //尾插数据psl->size++;  //有效数据个数+1
}
  • 测试其功能

4、顺序表尾删

不知道 SLDataType 是什么类型的数据,不能冒然的将顺序表最后一个数据赋值为 0,我们只需将有效数据个数 size 减 1 即可达到尾删的目的。

void SeqListPopBack(SeqList* psl)
{assert(psl != NULL);  //断言assert(psl->size > 0);  //顺序表不能为空//不知道SLDataType是什么类型的数据,不能冒然的赋值为0//psl->a[psl->size - 1] = 0;psl->size--;  //有效数据个数-1
}
  • 测试其功能

5、顺序表头插

因为顺序表是连续存储的,所以头插时要依次挪动数据

void SeqListPushFront(SeqList* psl, SLDataType x)
{assert(psl);  //断言CheckCapacity(psl);  //检查顺序表容量是否已满int i = 0;for (i = psl->size - 1; i >= 0; i--)  //顺序表中[0,size-1]的元素依次向后挪动一位{psl->a[i + 1] = psl->a[i];}psl->a[0] = x;  //头插数据psl->size++;  //有效数据个数+1
}
  • 测试其功能

6、顺序表头删

因为顺序表是连续存储的,所以头删时要依次挪动数据

void SeqListPopFront(SeqList* psl)
{assert(psl);  //断言assert(psl->size > 0);  //顺序表不能为空int i = 0;for (i = 1; i < psl->size; i++)  //顺序表中[1,size-1]的元素依次向前挪动一位{psl->a[i - 1] = psl->a[i];}psl->size--;  //有效数据个数-1
}
  • 测试其功能

7、打印顺序表

void SeqListPrint(const SeqList* psl)
{assert(psl != NULL);  //断言if (psl->size == 0)  //判断顺序表是否为空{printf("顺序表为空\n");return;}int i = 0;for (i = 0; i < psl->size; i++)  //打印顺序表{printf("%d ", psl->a[i]);}printf("\n");
}

8、在顺序表中查找指定值

int SeqListFind(const SeqList* psl, SLDataType x)
{assert(psl);  //断言int i = 0;for (i = 0; i < psl->size; i++){if (psl->a[i] == x){return i;  //查找到,返回该值在数组中的下标}}return -1;  //没有查找到
}
  • 测试其功能

9、在顺序表指定下标位置插入数据(要注意下int与size_t间的转换问题)

将指定位置后的所有数据依次向后挪动一位,初始代码如下:

int i = 0;
for (i = psl->size - 1; i >= pos; i--)psl->a[i + 1] = psl->a[i];
  • 原先这种写法,当顺序表为空 size = 0 时,会导致 i = -1,
    执行 i >= pos 时,i 被算术转换成无符号数,而无符号数的 -1 是一个值很大的正数,
    远大于 pos,满足条件进入循环,会造成越界访问
  • 注:转换并不会改变 i 本身的值,而是执行 i >= pos 时,生成一个临时的值与 pos 比较
  • 如果在顺序表头部插入数据 pos = 0,i 最终也会减减变成 -1,被算术转换后变成一个很大的数
  • 总结:避免负数给到无符号数,或者避免有符号数变成负数后,被算术转换或整型提升后,变成一个很大的数

下面这样写就避免 i 变成 -1 负数了

void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{assert(psl);  //断言assert(pos >= 0 && pos <= psl->size);  //检查pos下标的合法性CheckCapacity(psl);  //检查顺序表容量是否已满size_t i = 0;for (i = psl->size; i > pos; i--)  //将pos位置后面的数据依次向后挪动一位{psl->a[i] = psl->a[i - 1];}psl->a[pos] = x;  //插入数据psl->size++;  //有效数据个数+1
}
  • 测试其功能

  • 实现了此接口,顺序表头插相当于在下标为 0 位置处插入数据,可以改进下顺序表头插的代码:
void SeqListPushFront(SeqList* psl, SLDataType x)
{SeqListInsert(psl, 0, x);  //改造头插接口
}

10、在顺序表中删除指定下标位置的数据

void SeqListErase(SeqList* psl, size_t pos)
{assert(psl);  //断言assert(psl->size > 0);  //顺序表不能为空assert(pos >= 0 && pos < psl->size);  //检查pos下标的合法性size_t i = 0;for (i = pos + 1; i < psl->size; i++)  //将pos位置后面的数据依次向前挪动一位{psl->a[i - 1] = psl->a[i];}psl->size--;  //有效数据个数-1
}
  • 测试其功能

  • 实现了此接口,顺序表头删相当于删除下标为 0 位置处的数据,可以改进下顺序表头删的代码:
//顺序表头删
void SeqListPopFront(SeqList* psl)
{SeqListErase(psl, 0);  //改造头删接口
}

11、查看顺序表中有效数据个数

  • 可能大家会有一个疑问,我在主函数里面直接通过定义的结构体变量直接访问就好了呀,为啥还要弄一个函数嘞

  • 在数据结构中有一个约定,如果要访问或修改数据结构中的数据,不要直接去访问,要去调用它的函数来访问和修改,这样更加规范安全,也更方便检查是否出现了越界等一些错误情况

size_t SeqListSize(const SeqList* psl)
{assert(psl);  //断言return psl->size;
}
  • 补充:

越界不一定报错,系统对越界的检查是一种抽查

  • 越界读一般是检查不出来的

  • 越界写如果是修改到标志位才会检查出来

(系统在数组末尾后设的有标志位,越界写时,恰好修改到标志位了,就会被检查出来)

12、修改指定下标位置的数据

void SeqListAt(SeqList* psl, size_t pos, SLDataType x)
{assert(psl);  //断言assert(psl->size > 0);  //顺序表不能为空assert(pos >= 0 && pos < psl->size);  //检查pos下标的合法性psl->a[pos] = x;  //修改pos下标处对应的数据
}

【数据结构入门】顺序表(SeqList)详解(初始化、增、删、查、改)相关推荐

  1. 数据结构入门——顺序表(SeqList)详解(初始化、增、删、查、改)

    SeqList顺序表 1. 线性表介绍 2. 顺序表 2.1 顺序表的结构介绍 2.2 顺序表的定义和接口声明--`SeqList.h` 2.3 顺序表接口函数的具体实现--`SeqList.c` 3 ...

  2. 数据结构第二课 | 顺序表(详解)

    前言:Hello!大家好,我是@每天都要敲代码,上次我们讲了数据结构第一课时间复杂度和空间复杂度:不明白的小伙伴可以学习一遍时间复杂度和空间复杂度传送门:今天让我们开始一起学习数据结构第二课啦---- ...

  3. 数据结构_顺序表SeqList(C++

    数据结构_SeqList顺序表(C++实现 文章目录 数据结构_SeqList顺序表(C++实现 前言&注意事项 顺序表实现方法 总结 结束 前言&注意事项 有些函数没有修改成员数据的 ...

  4. 顺序表(详解)- C++(线性表顺序存储结构)

    问题引入 在数据结构中,线性表是一种很重要的线性结构.线性表分为多种类型,常见的如顺序表.链表等,如果此时此刻你对"顺序表(顺序存储)"感到困惑,那就继续看下去,我们一步一步去剖析 ...

  5. 顺序表 (数组) 详解

    数组 和 链表是最基本的数据结构,栈.队列.树.图等复杂数据结构都是基于数组或链表方式存储 线性表采用顺序存储的方式存储被称为顺序表,顺序表将表中结点依次存放在计算机内存中一组地址连续的存储单元中.从 ...

  6. 数据结构——哈希表的详解与实现

    数据结构--哈希表(HashTable) 1.前言 ​ 当我们频繁的查找数据中的某个元素时,我们通常会选择数组来存放数据,因为数组的的内存是连续的,可以直接通过下标访问数据,但是它添加和删除数据比较麻 ...

  7. JDBC从入门到熟练使用——功能类详解、增删改查(CRUD)、sql注入、事务、连接池

    一.JDBC入门 1.jdbc的概念 JDBC(Java DataBase Connectivity:java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系型数据库提供统一访问 ...

  8. mysql curd操作_(四):CURD操作详解(增删改查)

    基本操作:增删改查 一.增 增: insert 介绍: mongodb存储(单位)的是文档,. 文档是json格式的对象. 语法: db.collectionName.isnert(document) ...

  9. 零基础入门Python3-列表list详解

    list是一个有序的列表,比如:某个班级里面总共有5个学生,这些学生名字清单依次是:zhangsan.lisi.wangwu.meimei.huanhuan.这个名字清单就是一个有序的列表,给他们的名 ...

  10. pythonlist详解_零基础入门Python3-列表list详解

    list是一个有序的列表,比如:某个班级里面总共有5个学生,这些学生名字清单依次是:zhangsan.lisi.wangwu.meimei.huanhuan.这个名字清单就是一个有序的列表,给他们的名 ...

最新文章

  1. ubuntu18.04.4 pip3 换源
  2. .NET Core Generic Host项目使用Topshelf部署为Windows服务
  3. Android中通过Socket直接与RILD进行通信
  4. 【知识星球】重要小结与涨价预告
  5. linux安装多路径报错,linux多路径软件rdac安装方法
  6. 调用外部文件(ShellExecute)
  7. php语言中字符,PHP开发语言中字符窜的高效率写法
  8. 地雷会炸到自己吗_回顾自己曾经的往事 ——记双语学习有感
  9. Hook技术之Hook Activity
  10. 快排算法 java_快排算法的实现与讲解(java/C++)
  11. Docker 方式 MySQL 主从搭建
  12. 小米笔记本 Air 13.3 黑苹果教程
  13. 浩辰云建筑2021功能详细介绍
  14. [概率练习] n个小球放入m个盒子(8大类)
  15. 【网络设备】H3C FW V7:安全域与域间策略
  16. IDEA使用docker打包镜像
  17. 抽象手绘七夕情人节快闪PPT模板
  18. C#上位机编程常用方法
  19. html页面漏斗图,echarts 漏斗图示例
  20. linux系统服务器忘记密码怎么办

热门文章

  1. python单循环_「单循环赛」单循环赛制 - seo实验室
  2. linux免杀工具,kali 免杀工具shellter安装以及使用
  3. Jmeter编码格式
  4. 传奇私服网站php源码,传奇h5私服源码+教程
  5. wap2app是什么
  6. 码农小汪之理解Java注解。
  7. IIS DirectoryEntry
  8. 添加最顶层js广告_js实现网站最上边可关闭的浮动广告条代码
  9. premiere cc2014破解版|premiere cc2014绿色破解版下载
  10. webservice框架 java_java开发webservice的几种方式详解