【C语言数据结构(基础篇)】第二站:顺序表(上)

简介: 【C语言数据结构(基础篇)】第二站:顺序表

一、线性表

首先我们必须要了解的一个概念是线性表

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

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

顺序表就是数组,他的内存是连续的

下面就是一个链表的结构,当然或许你现在并不知道链表是什么,但是没有关系, 我们在后面的文章中将会花费大量的笔墨去详细讲解链表,这里你只需要大概看一眼就可以了,下图什么都看不懂是没有任何关系的。

二、顺序表的实现(概念以及静态顺序表)

对于顺序表的实现,我们采用分文件的形式来展现,在前面我们也已经利用了两个小游戏来让大家熟悉了分文件的写法,这里就不在赘述。如果还有不懂的地方,可以去C语言专栏去找到对应的文章。同样数据结构部分,为了规范我们的写法,我们全部采用大驼峰的命名风格

1.创建三个工程文件

如下图所示,我们创建好三个文件,分别是SeqList.c SeqList.h Test.c

SeqList.c用于顺序表的实现

SeqList.h用于顺序表的声明

Test.c用于测试顺序表

2.顺序表的概念

我们得先了解顺序表的概念,才能继续完成下去

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

顺序表一般可分为

静态顺序表:使用定长数组进行存储

动态顺序表:使用动态开辟的数组进行存储

3.顺序表的定义

我们先看静态顺序表是如何定义的,如下图所示,我们知道顺序表他也是用来存储数据的,既然是用来存储数据的,那么他必然要有一个数组来存储,然后还要有一个size来确定目前存储了一个数据。将这两个合并到一起就是一个结构体

虽然说这可以当作一个静态顺序表来进行定义,但是要真是这样定义,还是会出现很多问题,比如说哪一天想要存储100个数据了,那么我们上面这个就是写死了,根本没法改。所以我们可以使用define来进行优化,如下图所示

这样的话,确实,解决了一些后续无法增加数据的问题,但是如果突然不想要整型类型了,他想要char类型的话,那这个顺序表又废了,所以我们还得再次进行优化,我们可以使用一个typedef来重定义类型,如下图所示

当然这样其实还不够好,我们如果想要使用这个结构体的话,这个结构体类型太长了,我们写着写着都快烦死了。所以我们也可以对结构体进行typedef

如下图所示,我们让这个结构体类型变成了SL,当然这个有两种写法,一种是使用注释中的写法,另外一种就是在定义结构体的时候就typedef

最终代码如下(SeqList.h这个文件)

#pragma once
#define N 10
typedef int SQDateType;
typedef struct SeqList
{
  SQDateType arr[N];
  int size;
}SL;
//等价于typedef struct SeqList SL;

4.初始化顺序表

我们在说顺序表的概念的时候就提到过,顺序表是要实现增删查改四大功能的,但是在这之前,我们得先初始化顺序表

于是我们在头文件先进行一次初始化函数的声明

在SeqList.c文件中,我们就要实现这个功能了,如下图所示,我们使用一个memset函数,在这里可能有些人还不熟悉或者不太了解这个函数,我们可以现场学习一下 ,如下图所示,他是将传入的一个指针,从他开始的前num个字节都设置为value

所以下面中的意思就是将这个数组的全部元素设置为0,并且将size也就是目前的元素个数也设置为0

而对应这个函数也有他自己的头文件,我们为了方便,可以直接全部放在SeqList.h中

既然如此,那么我们就开始来测试一下这个函数究竟对不对

我们打完断点以后,直接使用F5,跳转至断点处,并打开调试的监视窗口,如下图所示,我们已经将顺序表给初始化完成了

我们开始进入我们的函数,我们发现,这块并没有将这个顺序表给初始化啊。这是为什么呢?

其实这块已经很多人发现了这个问题,因为我们传的其实是形参,形参的改变不会影响实参。我们就不该使用这个传值调用了,我们应该使用传址调用,我们现在将我们的代码都改为指针

而此时就能够完成我们的目标了,实现初始化顺序表

顺序表初始化接口代码为

//顺序表初始化
void SeqListInit(SL* ps)
{
  memset(ps->arr, 0, sizeof(SQDateType) * N);
  ps->size = 0;
}

5.静态顺序表的尾插

在我们将顺序表给初始化以后,我们现在需要的就是试下顺序的表的增删查改,当然我们在讲述顺序表的概念的时候,提到了这两个关键点:

1.连续的物理存储地址------数组

2.数据必须是从头开始,依次存储------不能在数组的第一个元素插入一个,然后第二个又跑到了第三个元素上

那么我们就先来定义一下我们的接口函数,如下图所示,这里我们的命名与c++的标准库一致,让我们的程序更加规范

不过既然有尾插,那么与之对应的就有头插,尾删,以及头删,不妨我们也直接定义好他们的接口吧,如下图所示

那么我们先来实现尾插,如何实现尾插呢?我们可以试着画图理解一下,如下图所示,我们假设数组已经存储了5个元素了,此时他的size就是5,那么这个5刚好就是下一个数据的下标,所以我们就想到了,有了下标了,那不就是直接插入数据了吗。插入完之后再让size++就行了吗,确实是这样的。

我们来实现一下我们的想法,如下图所示

但是这样存在一个问题,如果顺序表满了呢?我们可以实验一下

我们进行调试,如下图所示,顺序表已经初始化完成

我们使用F10不进入函数内部进行调试,我们可以发现现在即将越界

此时已经越界了,我们发现arr[10]他与size进行同步变化,size直接变成了12,而不是变成了11,这个问题,在我们之前讲解调试的时候已经提到过了,这就是数组越界所带来的风险。size的数据已经被破坏了。所以这个顺序表已经废了

那么我们该怎么办呢?我们可以在顺序表满的时候提示一句顺序表满了,然后就直接返回,直接拦截这个错误的信息,不让他插入进去,所以实现如下

这个时候我们再次调试一下,此时我们发现,拦截成功了。

其实到这块我们也发现一个问题,那就是静态顺序表不太好,要么消耗的空间太大,要么空间不够。总之很麻烦。而我们一般下也不会去使用静态的顺序表,我们都是使用动态的顺序表,可以进行扩容的顺序表。

所以我们在这里就不在继续进行实现静态的顺序表的,我们转而实现动态的顺序表

相关文章
|
27天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
123 9
|
26天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
63 16
|
26天前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
86 8
|
29天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
55 4
|
29天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
42 0
|
18天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
23 1
|
5天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
26 5
|
21天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
24天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
26天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
47 4