【数据结构初阶】顺序表

各位读者老爷好,又见面了哈!鼠鼠我呀现在基于C语言浅浅介绍一下数据结构初阶中的顺序表,希望对你有所帮助!

目录

1.线性表 

2.顺序表

2.1概念即结构

2.2动态顺序表接口的实现

2.2.1定义顺序表

2.2.2初始化

2.2.3销毁 

2.2.4打印

2.2.5尾插

2.2.6头插 

2.2.7头删

2.2.8尾删 

2.2.9顺序表查找

 2.2.10顺序表在下标pos位置插入数据x

2.2.11 顺序表删除下标pos位置的值

3.顺序表使用 

3.1test.c

3.2SeqList.h

3.3SeqList.c

4.小知识累积(与顺序表无关)

4.1数组越界一定会报错吗?

4.2数组的下标为什么不从1开始而要从0开始呢? 

5.ending

额额额,好哈!顺序表是线性表的一种哈!那我们看看下面是线性表!

1.线性表 

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

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

咱们下面介绍的顺序表是一种在逻辑上和物理结构上是连续的。

2.顺序表

2.1概念即结构

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

与数组不同的是,数组可以在不越界的情况下任意位置存储数据,而顺序表要求数据只能从头开始连续存储。

 顺序表一般可以分为:

1.静态顺序表:使用定长数组存储数据

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

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

//顺序表的动态存储
#typedef int SLDataType
typedef struct SeqList
{
SLDataType*array;//指向动态开辟的数组
size_t size;//有效数据的个数
size_t capicity;//容量空间的大小
}SeqList;

2.2动态顺序表接口的实现

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

2.2.1定义顺序表
typedef int SLDateType;typedef struct SeqList
{SLDateType* a;int size;                                           //有效数据int capaticy;                                       //空间容量
}SeqList;

根据上面定义的顺序表,咱们创建一个顺序表s1,并实现该顺序表一系列接口实现。

SeqList s1;//顺序表
2.2.2初始化
void SeqListInit(SeqList* ps)                               //初始化
{assert(ps);ps->a = NULL;ps->capaticy = 0;ps->size = 0;
}

咱们使用顺序表之前需要将有效数据size和空间容量capaticy置零,同时不妨将指向动态开辟数组的指针a置为NULL。

ps:这个函数的参数接收的是上面创建的顺序表s1的地址。

question:实现这个初始化函数参数为啥是s1的地址而不是s1呢?

answer:因为形参是实参的一份临时拷贝,形参的改变不会影响实参。就以这个初始化函数为例,如果这个函数参数是"SeqList s2"的话 ,虽然这个函数中将s2的成员a置为NULL、将s2的成员size和capaticy都置为零,但根本影响不了顺序表s1的成员a、size和capaticy。

就是因为这个原因,下面接口的实现都是采用传递s1的地址。

2.2.3销毁 
void SeqListDestroy(SeqList*ps)                              //销毁
{assert(ps);if (ps->a != NULL){ps->a = NULL;ps->capaticy = 0;ps->size = 0;}
}

咱们如果不再使用顺序表的话,因为顺序表的成员a指向动态开辟的数组,所以最好将这块空间free掉,size和capaticy也最好置零。

 ps:这个函数的参数接收的是上面创建的顺序表s1的地址。

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

必要时可以将存储在顺序表的数据打印出来看看,因为有size个数据,所以循环打印size次即可将数据全部打印出来。

 ps:这个函数的参数接收的是上面创建的顺序表s1的地址。

2.2.5尾插
void SLCheckcapacity(SeqList* ps)                        //扩容
{assert(ps);if (ps->size == ps->capaticy){int newcapaticy = (ps->capaticy == 0) ? 4 : 2 * (ps->capaticy);SLDateType* tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * newcapaticy);if (tmp == NULL){perror("calloc fail");return;}ps->a = tmp;ps->capaticy = newcapaticy;}
}
void SeqListPushBack(SeqList* ps, SLDateType x)               //尾插
{assert(ps);SLCheckcapacity(ps);ps->a[ps->size] = x;ps->size += 1;
}

在顺序表的末尾(下标为size处)处插入数据时直接插入,size加一即可。但要考虑顺序表容量空间是否足够,所以要调用扩容函数SLCheckcapacity,扩容函数的实现大致时当顺序表的size和capaticy一样时,调用realloc函数二倍扩容。 

ps:尾插函数的参数接收的是上面创建的顺序表s1的地址。

2.2.6头插 
void SLCheckcapacity(SeqList* ps)                        //扩容
{assert(ps);if (ps->size == ps->capaticy){int newcapaticy = (ps->capaticy == 0) ? 4 : 2 * (ps->capaticy);SLDateType* tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * newcapaticy);if (tmp == NULL){perror("calloc fail");return;}ps->a = tmp;ps->capaticy = newcapaticy;}
}
void SeqListPushFront(SeqList* ps, SLDateType 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++;
}

顺序表头部插入数据就是将第二个及以后的数据均后移,在将所需头插数据x插入到顺序表头部,size加一即可,同样要考虑容量问题。

 ps:头插函数的第一个参数接收的是上面创建的顺序表s1的地址,第二个参数是所需头插数据。

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

顺序表删除头部数据也很简单,将第二个及以后的数据均向前覆盖,在将size减一即可。但是需要注意的是,如果size为零的话说明没有数据就不要再删了,用assert断言一下就行。

  ps:这个函数的参数接收的是上面创建的顺序表s1的地址。

2.2.8尾删 
void SeqListPopBack(SeqList* ps)                              //尾删
{assert(ps);assert(ps->size >0);ps->size--;
}

顺序表尾部删除数据最简单,直接将size减一就行,但和头删一样,用assert断言一下防止删空了。

  ps:这个函数的参数接收的是上面创建的顺序表s1的地址。

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

我们也许需要在顺序表中查找某个数据,所以遍历这个顺序表的数据即可,找到返回该数据下标,找不到返回-1。

 ps:该函数的第一个参数接收的是上面创建的顺序表s1的地址,第二个参数是所需查找的数据。

 2.2.10顺序表在下标pos位置插入数据x
void SLCheckcapacity(SeqList* ps)                        //扩容
{assert(ps);if (ps->size == ps->capaticy){int newcapaticy = (ps->capaticy == 0) ? 4 : 2 * (ps->capaticy);SLDateType* tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * newcapaticy);if (tmp == NULL){perror("calloc fail");return;}ps->a = tmp;ps->capaticy = newcapaticy;}
}
void SeqListInsert(SeqList* ps, int pos, SLDateType x)           //顺序表在下标pos位置插入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以后的数据都往后挪,在将所需插入的数据x插入到下标pos的位置,在将size加一即可。但有几处细节要注意:1.pos只能在0和size之间(包括0和size), 否则就越界访问了,所以利用assert断言一下;2.防止顺序表容量不足,调用扩容函数。

ps:该函数的第一个参数接收的是上面创建的顺序表s1的地址,第二个参数是插入位置的下标,第三个参数是需插入的数据。

2.2.11 顺序表删除下标pos位置的值
void SeqListErase(SeqList* ps, int pos)    // 顺序表删除下标pos位置的值
{assert(ps);assert(pos >= 0 && pos< ps->size);int begin = pos + 1;while (begin <= ps->size - 1){ps->a[begin - 1] = ps->a[begin];begin++;}ps->size--;
}

这个接口实现大致就是将下标pos以后的数据均向前覆盖,size减一即可,若需删除最后一个(下标为size-1)数据 ,无需覆盖,直接size减一即可。细节:下标pos只能在0到size之间(包括0),否则会越界访问,利用assert断言。

ps:该函数第一个参数接收的是上面创建的顺序表s1的地址,第二个参数是需删除数据的下标。

3.顺序表使用 

咱们顺序表接口就实现完了。可以写一个接口来对接上面的所以接口,以实现顺序表的增删查改及初始化、销毁、打印等功能。鼠鼠我写了一个,可以参考参考哈:

3.1test.c

#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
void menu()
{printf("**********************\n");printf("********0.退出********\n");printf("****1.头插  2.头删****\n");printf("****3.尾插  4.尾删****\n");printf("****5.查找  6.打印****\n");printf("*7.在下标pos位置插入值\n");printf("*8.删除下标pos位置的值\n");printf("**********************\n");
}
int main()
{SeqList s1;SeqListInit(&s1);int input;do {menu();printf("请输入你想操作的数字:->");scanf("%d", &input);if (input == 0){SeqListDestroy(&s1);printf("\n");break;}else if (input == 1){SLDateType x = 0;printf("请输入你要插入的值:->");scanf("%d", &x);SeqListPushFront(&s1, x);printf("\n");}else if (input == 2){SeqListPopFront(&s1);printf("\n");}else if (input == 3){SLDateType x = 0;printf("请输入你要插入的值:->");scanf("%d", &x);SeqListPushBack(&s1, x);printf("\n");}else if (input == 4){SeqListPopBack(&s1);printf("\n");}else if (input == 5){SLDateType x = 0;printf("请输入你要查找的值:->");scanf("%d", &x);int flag=SeqListFind(&s1, x);if (flag!=-1){printf("你要查找的值下标是%d\n", flag);}else{printf("找不到!\n");}printf("\n");}else if (input == 6){SeqListPrint(&s1);printf("\n");}else if (input == 7){SLDateType x = 0; int pos = 0;printf("请分别输入你要插入的值及插入的下标:->");scanf("%d %d", &x, &pos);SeqListInsert(&s1,pos,x);printf("\n");}else if (input == 8){int pos = 0;printf("请输入你要删除值的下标:->");scanf("%d", &pos);SeqListErase(&s1,pos);printf("\n");}else{printf("输入错误,请重新输入:->");}} while (input);return 0;
}

3.2SeqList.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int SLDateType;typedef struct SeqList
{SLDateType* a;int size;                                           //有效数据int capaticy;                                       //空间容量
}SeqList;void SeqListInit(SeqList* ps);                          //初始化void SeqListDestroy(SeqList* ps);                       //销毁void SeqListPrint(SeqList* ps);                         //打印void SeqListPushBack(SeqList* ps, SLDateType x);        //尾插void SeqListPushFront(SeqList* ps, SLDateType x);       //头插void SeqListPopFront(SeqList* ps);                      //头删void SeqListPopBack(SeqList* ps);                       //尾删int SeqListFind(SeqList* ps, SLDateType x);             //顺序表查找void SeqListInsert(SeqList* ps, int pos, SLDateType x); // 顺序表在下标pos位置插入xvoid SeqListErase(SeqList* ps, int pos);                // 顺序表删除下标pos位置的值

3.3SeqList.c

#define _CRT_SECURE_NO_WARNINGS 
#include"SeqList.h"void SLCheckcapacity(SeqList* ps)                        //扩容
{assert(ps);if (ps->size == ps->capaticy){int newcapaticy = (ps->capaticy == 0) ? 4 : 2 * (ps->capaticy);SLDateType* tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * newcapaticy);if (tmp == NULL){perror("calloc fail");return;}ps->a = tmp;ps->capaticy = newcapaticy;}
}void SeqListInit(SeqList* ps)                               //初始化
{assert(ps);ps->a = NULL;ps->capaticy = 0;ps->size = 0;
}void SeqListDestroy(SeqList*ps)                              //销毁
{assert(ps);if (ps->a != NULL){ps->a = NULL;ps->capaticy = 0;ps->size = 0;}
}void SeqListPrint(SeqList* ps)                               //打印
{assert(ps);int i = 0;for (i = 0; i < ps->size; i++){printf("%d ", ps->a[i]);}printf("\n");
}void SeqListPushBack(SeqList* ps, SLDateType x)               //尾插
{assert(ps);SLCheckcapacity(ps);ps->a[ps->size] = x;ps->size += 1;
}void SeqListPushFront(SeqList* ps, SLDateType 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 SeqListPopFront(SeqList* ps)                             //头删
{assert(ps);assert(ps->size > 0);int begin = 0;while (begin<ps->size-1){ps->a[begin] = ps->a[begin+1];begin++;}ps->size--;
}void SeqListPopBack(SeqList* ps)                              //尾删
{assert(ps);assert(ps->size >0);ps->size--;
}int SeqListFind(SeqList* ps, SLDateType x)                     //顺序表查找
{assert(ps);int i = 0;for (i = 0; i < ps->size; i++){if (x == ps->a[i]){return i;//找得到返回下标}}return -1;//找不到返回-1
}void SeqListInsert(SeqList* ps, int pos, SLDateType x)           //顺序表在下标pos位置插入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 SeqListErase(SeqList* ps, int pos)                          // 顺序表删除下标pos位置的值
{assert(ps);assert(pos >= 0 && pos< ps->size);int begin = pos + 1;while (begin <= ps->size - 1){ps->a[begin - 1] = ps->a[begin];begin++;}ps->size--;
}

 各位读者老爷可以将这三个文件放到一个工程下玩玩哦!

4.小知识累积(与顺序表无关)

4.1数组越界一定会报错吗?

answer:越界读基本不会报错。越界写可能会报错。越界的检查是一种抽查行为,就像查酒驾一样,比如数组通常会在数组后面设置一些标记位,一旦标记位的 值被更改就会报错,所以一般在数组末尾附近越界写会报错,但越界太远写就基本不报错了(跳过了标记位)。当然不同的编译器对越界的检查不同。这里只是对越界报错的一种认知。

4.2数组的下标为什么不从1开始而要从0开始呢? 

因为通过下标访问数组本质是指针访问,数组下标从0开始是要和指针的设计自恰!

a[i]等价于*(a+i),只有当下标从0开始时,当i=0时,a[0]=*(a+0)才解释得通。

5.ending

鼠鼠我才疏学浅,且时间紧迫,如有不足,恳请斧正,谢谢哈哈哈!

懂我意思吧?

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

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

相关文章

asp.net外卖网站系统VS开发mysql数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net外卖网站系统 是一套完善的web设计管理系统&#xff0c;系统采用mvc模式&#xff08;BLLDALENTITY&#xff09;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为mysql&#xff0c;使用c#语…

【Unity每日一记】“调皮的协程”,协程和多线程的区别在哪里

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

Scrum Master 如何更好的支持PO?

在过去几年中&#xff0c;和许多Scrum Master交流时&#xff0c;我遇到一个令人担忧的模式。虽然我们有Scrum指南和其他补充资源&#xff0c;许多Scrum Master&#xff0c;特别是刚起步的Scrum Master们&#xff0c;还在日复一日的为如何帮助Product Owner而挣扎着。 以下是我…

python爬虫hook定位技巧、反调试技巧、常用辅助工具

一、浏览器调试面板介绍 二、hook定位、反调试 Hook 是一种钩子技术&#xff0c;在系统没有调用函数之前&#xff0c;钩子程序就先得到控制权&#xff0c;这时钩子函数既可以加工处理&#xff08;改变&#xff09;该函数的执行行为&#xff0c;也可以强制结束消息的传递。简单…

掌握未来技术趋势:深度学习与量子计算的融合

掌握未来技术趋势&#xff1a;深度学习与量子计算的融合 摘要&#xff1a;本博客将探讨深度学习与量子计算融合的未来趋势&#xff0c;分析这两大技术领域结合带来的潜力和挑战。通过具体案例和技术细节&#xff0c;我们将一睹这两大技术在人工智能、药物研发和金融科技等领域…

CnosDB 在最近新发布的 2.4.0 版本中增加对时空函数的支持。

CnosDB 在最近新发布的 2.4.0 版本中增加对时空函数的支持。 概述 时空函数是一种用于描述时空结构和演化的函数。它在物理学、数学和计算机科学等领域中都有广泛的应用。时空函数可以描述物体在时空中的位置、速度、加速度以及其他相关属性。 用法 CnosDB 将使用一种全新的…

[工业自动化-16]:西门子S7-15xxx编程 - 软件编程 - 西门子仿真软件PLCSIM

目录 前言&#xff1a; 一、PLCSIM仿真软件 1.1 PLCSIM仿真软件基础版&#xff08;内嵌&#xff09; 1.2 PLCSIM仿真软件与PLCSIM仿真软件高级版的区别&#xff1f; 1.3 PLCSIM使用 前言&#xff1a; PLC集成开发环境是运行在Host主机上&#xff0c;Host主机与PLC可以通过…

Android---内存泄漏的优化

内存泄漏是一个隐形炸弹&#xff0c;其本身并不会造成程序异常&#xff0c;但是随着量的增长会导致其他各种并发症&#xff1a;OOM&#xff0c;UI 卡顿等。 为什么要将 Activity 单独做预防&#xff1f; 因为 Activity 承担了与用户交互的职责&#xff0c;因此内部需要持有大…

从0到1实现一个前端监控系统(附源码)

目录 一、从0开始 二、上报数据方法 三、上报时机 四、性能数据收集上报 收集上报FP 收集上报FCP 收集上报LCP 收集上报DOMContentLoaded 收集上报onload数据 收集上报资源加载时间 收集上报接口请求时间 五、错误数据收集上报 收集上报资源加载错误 收集上报js错…

clouldcompare工具使用

文章目录 1.界面1.1 布局1.3 视觉显示方向1.4 放大镜1.5 建立旋转中心2.快速入门2.1 剪裁2.2 多点云拼接 1.界面 1.1 布局 参考&#xff1a;https://blog.csdn.net/lovely_yoshino/article/details/129595201 1.3 视觉显示方向 1.4 放大镜 1.5 建立旋转中心 2.快速入门 2.1 …

网络原理-UDP/TCP详解

一. UDP协议 UDP协议端格式 由上图可以看出&#xff0c;一个UDP报文最大长度就是65535. • 16位长度&#xff0c;表示整个数据报&#xff08;UDP首部UDP数据&#xff09;的最大长度&#xff08;注意&#xff0c;这里的16位UDP长度只是一个标识这个数据报长度的字段&#xff0…

灵活运用Vue指令:探究v-if和v-for的使用技巧和注意事项

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 一、作…