c语言数据结构与算法之顺序表的定义实现详解

 更新时间:2023年08月08日 09:15:38   作者:程序喵正在路上  
这篇文章主要介绍了c语言数据结构与算法之顺序表的定义实现详解,用顺序存储的方式实现线性表顺序存储,把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现,需要的朋友可以参考下

顺序表的定义

顺序表 —— 用顺序存储的方式实现线性表顺序存储。

把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现

线性表是具有相同数据类型的 n (n≥0) 个数据元素的有限序列,数据类型相同说明每个数据元素所占的空间是一样大的

我们假设线性表第一个元素的存放位置(即首地址)是 LOC(L)LOClocation 的缩写

那么第二个元素的存放位置就是 LOC(L)+数据元素的大小

第三个元素的存放位置就是 LOC(L)+2*数据元素的大小,依此类推…

那么,我们又怎么知道所存放的数据元素的大小呢?

学过 C语言 的小伙伴肯定知道,C语言 中有一个函数 sizeof() 可以帮上我们的忙,我们用 sizeof(ElemType) 这样调用的方式就能得到对应数据元素的大小,其中的 ElemType 就是你的顺序表中存放的数据元素类型,比如 sizeof(int) = 4B ,一个整型一般是 4 个字节;此外,我们还可以传自己定义的类型进去,比如,我们定义一个 Customer 类型

typedef struct {
	int num;	//号数
	int people; //人数
} Customer;

相应地,我们使用 sizeof(Customer) 就可以得到这个数据类型的大小为 8B

顺序表的实现 —— 静态分配

#define MaxSize 10				//定义最大长度
typedef struct {
	ElemType data[MaxSize];		//用静态的 “数组” 存放数据元素
	int length;					//顺序表的当前长度
} SqList;						//顺序表的类型定义(静态分配方式)

ElemType data[MaxSize]; 会给各个数据元素分配连续的存储空间,大小为 MaxSize*sizeof(ElemType)

Sq:sequence —— 顺序,序列

#include <stdio.h>
#define MaxSize 10				//定义最大长度
typedef int ElemType;
typedef struct {
	ElemType data[MaxSize];		//用静态的 “数组” 存放数据元素
	int length;					//顺序表的当前长度
} SqList;						//顺序表的类型定义
//基础操作 —— 初始化一个顺序表
void InitList(SqList &L) {
	for(int i = 0; i < MaxSize; i++) {
		L.data[i] = 0;		//将所有数据元素设置为默认初始值
	}
	L.length = 0;
}
//主函数
int main() {
	SqList L;		//声明一个顺序表
	InitList(L);	//初始化顺序表
	//其他操作
	return 0;
}

如果在初始化顺序表中不给 data 数组赋值为 0,会怎么样呢?

#include <stdio.h>
#define MaxSize 10				//定义最大长度
typedef int ElemType;
typedef struct {
	ElemType data[MaxSize];		//用静态的 “数组” 存放数据元素
	int length;					//顺序表的当前长度
} SqList;						//顺序表的类型定义
//基础操作 —— 初始化一个顺序表
void InitList(SqList &L) {
	L.length = 0;
}
//主函数
int main() {
	SqList L;		//声明一个顺序表
	InitList(L);	//初始化顺序表
	//尝试“违规”打印整个数组
	for(int i = 0; i < MaxSize; i++) {
		printf("data[%d]=%d\n", i, L.data[i]);
	}
	return 0;
}

运行这段代码,你会看到很奇怪的数据,或许和我的运行结果不太一样,这是正常的

在这里插入图片描述

为什么会显示这些奇怪的数据呢?

其实啊,程序在给我们这个数组分配内存的时候,我们并不知道这块内存里面之前存储过什么,如果我们不设置数据元素的默认值,就会出现这样的结果

其实,我们这样访问也是违规的,因为初始化顺序表的时候 length 是为 0 的,我们要遍历的条件应该是 i < L.length; 才对,所以数据元素的初始化是可以省略的,不过 length 的初始化这一步骤就不能省略了

如果我们定义的 “数组” 存满了,怎么办呢?

建议直接放弃治疗,顺序表的表长刚开始确定后就无法更改了,因为我们是静态分配空间

有小伙伴就说,那我一开始就声明一个很长的顺序表不就行了,但这样的后果是会浪费我们的内存空间,从这里我们就可以看出来静态分配的局限性了

顺序表的实现 —— 动态分配

#define InitSize 10			//顺序表的初始长度
typedef struct {
	ElemType *data;			//指示动态分配数组的指针
	int MaxSize;			//顺序表的最大容量
	int length;				//顺序表的当前长度
} SqList;					//顺序表的类型定义

C语言 中提供了 mallocfree 这两个函数来让我们动态申请和释放内存空间

malloc 会申请一整片连续的存储空间,然后返回一个指向这一片存储空间开始地址的指针,同时需要我们强制转型为定义的数据元素类型指针

malloc 函数的参数,指明了要分配多大的连续内存空间

L.data = (ElemType *) malloc(sizeof(ElemType) * InitSize);

如果你学习过 C++,那你也可以使用 newdelete 这两个函数来实现同样的功能

#include <stdio.h>
#include <stdlib.h>				//使用 malloc 和 free 函数所需的头文件
#define InitSize 10				//默认最大长度
typedef int ElemType;			//方便我们改变类型
typedef struct {
	ElemType *data;				//指示动态分配数组的指针
	int MaxSize;				//顺序表的最大容量
	int length;					//顺序表的当前长度
} SqList;						//顺序表的类型定义
//初始化顺序表
void InitList(SqList &L);
//增加动态数组的长度
void IncreaseSize(SqList &L, int len);
//主函数
int main() {
	SqList L;			//声明一个顺序表
	InitList(L);		//初始化顺序表
	IncreaseSize(L, 5);
	return 0;
}
//初始化顺序表
void InitList(SqList &L) {
	//用 malloc 函数申请一片连续的存储空间
	L.data = (ElemType *)malloc(sizeof(ElemType)* InitSize);
	L.length = 0;
	L.MaxSize = InitSize;
}
//增加动态数组的长度
void IncreaseSize(SqList &L, int len) {
	ElemType *p = L.data;				//将L数据存储起来
	L.data = (ElemType *)malloc(sizeof(ElemType)* (L.MaxSize + len));
	for (int i = 0; i < L.length; i++) {
		L.data[i] = p[i];				//将数据复制到新区域
	}
	L.MaxSize = L.MaxSize + len;		//顺序表最大长度增加 len
	free(p);							//释放临时的内存空间
}

注意:realloc 函数也可以实现,但建议初学者使用 mallocfree 更能理解背后的过程

顺序表的特点:

  • 随机访问,既可以在 O(1) 时间内找到第 i 个元素,代码实现为 data[i - 1]
  • 存储密度高,每个节点只存储数据元素
  • 拓展容量不方便,即便采用动态分配的方式实现,拓展长度的时间复杂度也比较高
  • 插入、删除操作不方便,需要移动大量元素

到此这篇关于c语言数据结构与算法之顺序表的定义实现详解的文章就介绍到这了,更多相关c语言顺序表的定义内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++ std::map几种遍历方式(正序倒序)

    C++ std::map几种遍历方式(正序倒序)

    这篇文章主要介绍了C++ std::map几种遍历方式,包含正序遍历和倒序遍历,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-02-02
  • C++临时性对象的生命周期详细解析

    C++临时性对象的生命周期详细解析

    临时性对象的被摧毁,应该是对完整表达式(full-expression)求值过程中的最后一个步骤。该完整表达式造成临时对象的产生
    2013-09-09
  • c/c++内存分配大小实例讲解

    c/c++内存分配大小实例讲解

    在本篇文章里小编给大家整理了一篇关于c/c++内存分配大小实例讲解内容,有需要的朋友们可以跟着学习参考下。
    2021-11-11
  • C语言PlaySound函数使用方法

    C语言PlaySound函数使用方法

    这篇文章介绍了C语言PlaySound函数的使用方法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-12-12
  • C语言单值二叉树真题讲解

    C语言单值二叉树真题讲解

    单值二叉树你可能之前没见过,如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树,让我们通过一个真题来深刻了解它吧
    2022-04-04
  • AVX2指令集浮点乘法性能分析

    AVX2指令集浮点乘法性能分析

    这篇文章主要为大家介绍了AVX2指令集浮点乘法性能分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • c语言 malloc函数详解

    c语言 malloc函数详解

    这篇文章主要介绍了c语言 malloc函数详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • C++中getline()、gets()等函数的用法详解

    C++中getline()、gets()等函数的用法详解

    这篇文章主要介绍了C++中getline()、gets()等函数的用法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-02-02
  • 二分图匹配实例代码及整理

    二分图匹配实例代码及整理

    这篇文章主要介绍了二分图匹配实例代码及整理的相关资料,这里提供了三种方法包括匈牙利算法,KM算法,多重匹配,需要的朋友可以参考下
    2017-07-07
  • C语言选择、循环、函数、数组与操作符

    C语言选择、循环、函数、数组与操作符

    这篇文章主要介绍了C语言选择、循环、函数、数组与操作符,文章基于C语言展开对主题的详细介绍,下文内容需要的小伙伴可以参考一下
    2022-04-04

最新评论