数据结构:线性表之-顺序表

news2024/11/15 23:21:55

目录

1.线性表概念

1.1 什么是顺序列表

1.2 线性表

2.顺序表实现

将有以下功能:

详细过程

顺序表的动态存储

顺序表初始化

尾插

扩容

头插

更改后的尾插

尾删

头删

打印

释放内存

优化顺序表 (任意位置插入删除)

优化后的头插尾插

优化后的头删尾删

查找和删除

进行装饰(菜单)

成品

SeqList.h

SeqList.c

Test.c:


1.线性表概念

1.1 什么是顺序列表

顺序列表(Sequential List)是一种使用连续的内存空间存储元素的线性数据结构。顺序列表中的元素按照其在内存中的物理顺序依次排列,同时通过索引来访问元素。

顺序列表可以使用数组来实现,数组的下标就是元素的索引。由于数组具有随机访问的特性,即可以通过索引直接访问元素,因此顺序列表在查找指定位置的元素时具有较高的效率。

顺序列表的特点包括:

  1. 连续的内存空间:顺序列表中的元素在内存中是连续存储的,这样可以通过索引进行快速访问,提高了访问效率。

  2. 固定大小:顺序列表的大小在创建时就确定,一旦分配了固定大小的内存空间,就无法自动扩展或缩小。需要预估元素的个数,以避免空间浪费或溢出。

  3. 随机访问效率高:由于顺序列表基于数组实现,并支持随机访问,可以在O(1)的时间复杂度内获取指定位置的元素值。

  4. 插入和删除的效率较低:当需要在顺序列表的中间位置插入或删除元素时,需要移动部分元素,导致时间复杂度为O(n)。因此,在有频繁的插入和删除操作时,顺序列表的效率可能较低。

需要注意的是,顺序列表适用于元素个数固定且随机访问较为频繁的场景。当需要频繁进行插入和删除操作,或者元素个数不确定时,可以考虑其他数据结构,如链表。

1.2 线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使
用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,
线性表在物理上存储时,通常以数组和链式结构的形式存储。

2.顺序表实现

将有以下功能:

// 顺序表的动态存储
typedef struct SeqList

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

详细过程

定义三个文件:
头文件 SeqList.h
函数的实现SeqList.c
代码的测试 Test.c

顺序表的动态存储

//SeLqist.h
#define N 200
typedef int SLDataType;

//静态顺序表 -- N太小,可能不够用 N太大,可能浪费空间
//struct SeqList
//{
//	SLDataType a[N];
//	int size;
//	int capa;
//};

//动态顺序表
typedef struct SeqList
{
	SLDataType* a;// 指向数组的指针
	int size;	  // 数据个数
	int capacity;//  容量-空间大小
}SL;

顺序表初始化

//SeqList.c
void SLInit(SL* ps)
{
	ps->a = NULL;
	ps->size =ps->capacity= 0;
}

尾插

void SLPushBack(SL* ps, SLDataType x)
{
	//检查容量空间,满了扩容
	if (ps->capacity == ps->size)
	{
		int newCapacity = 0;
		if (ps->capacity == 0)
			newCapacity = ps->capacity = 4;
		else
			newCapacity = ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			//exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
	}
	ps->a[ps->size] = x;
	ps->size++;
}

扩容

//动态增容
void SLCheckCapacity(SL* ps)
{
   //检查容量空间,满了扩容
   if (ps->capacity == ps->size)
   {
   	int newCapacity = 0;
   	if (ps->capacity == 0)
   		newCapacity = ps->capacity = 4;
   	else
   		newCapacity = ps->capacity * 2;
   	SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
   	if (tmp == NULL)
   	{
   		printf("realloc fail\n");
   		//exit(-1);
   	}
   	ps->a = tmp;
   	ps->capacity = newCapacity;
   }
}

头插

因为多处要进行数据扩容,故将数据扩容单独用写为一个函数

void SLPushBack(SL* ps, SLDataType x)
{
	SLCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}

更改后的尾插

//尾插
void SLPushBack(SL* ps, SLDataType x)
{
	SLCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}

尾删

void SLPopBack(SL* ps)
{
	//尾部要删除的数字无需重新定义数字,意义不大
	//只需将 size-- 即可(要防止越界)
	assert(ps->size>0);//防止空了还继续删除
	ps->size--;
}

头删

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

打印

//打印
void SLPrint(SL* ps)
{
	assert(ps!=NULL);

	for (int i = 0;i < ps->size;i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

释放内存

//释放内存
void SLDestory(SL* ps)
{
	assert(ps != NULL);

	if (ps->a)
	{
		free(ps->a);
		ps->a = NULL;
		ps->capacity = ps->size = 0;
	}
}

优化顺序表 (任意位置插入删除)

增加顺序表功能:在中间部分 插入/删除 数字,也可简化头尾插删代的码量

//任意位置插入 (插入数据都要防止越界)
void SLInsert(SL* ps, int pos, SLDataType 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 SLErase(SL* ps, int pos)
{
	assert(pos >= 0 && pos < ps->size);
	
	int begin = pos;
	while (begin<ps->size)
	{
		ps->a[begin] = ps->a[begin + 1];
		++begin;
	}
	ps->size--;
}

既然已经做到在任意位置可以插入代码,则可以对之前写的代码进行简化:

优化后的头插尾插

//尾插
void SLPushBack(SL* ps, SLDataType x)
{
	SLInsert(ps, ps->size, x);
}

//头插
void SLPushFront(SL* ps, SLDataType x)
{
	SLInsert(ps, 0, x);
}

优化后的头删尾删

//尾删
void SLPopBack(SL* ps)
{
	SLErase(ps, ps->size - 1);
}

//头删
void SLPopFront(SL* ps)
{
	SLErase(ps, 0);
}

查找和删除

//查找
int SLFind(SL* ps, SLDataType x)
{
	for (int i = 0; i <ps->size; i++)
	{
		if (ps->a[i] == x)
			return i;
	}
	return -1;//没找到
}

//修改
int SLModify(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);

	ps->a[pos] = x;
}

测试该两项功能test.c:

//
int x = 0;
printf("请输入你要删除的值:>");
scanf("%d", &x);
int pos = SLFind(&sl, x);
if (pos != -1)
{
	SLErase(&sl, pos);
}
else
	printf("没有找到%d\n",x);
SLPrint(&sl);
//
int y, z;
printf("请输入你要修改的值和修改后的值:>");
scanf("%d %d", &y,&z);
pos = SLFind(&sl, y);
if (pos != -1)
{
	SLModify(&sl, pos,z);
}
else
	printf("没有找到%d\n", y);
SLPrint(&sl);
//
int f = 0;
printf("请输入你要删除的值,并删除所有与之相同的值:>");
scanf("%d", &f);
pos = SLFind(&sl, f);
while (pos!=-1)
{
	SLErase(&sl, pos);
	pos = SLFind(&sl, f);
}

进行装饰(菜单)

void menu()
{
	printf("*******************************\n");
	printf("1.头插  2.尾插	     3.查找 \n");
	printf("4.删除  5.连续删除   6.修改  \n");
	printf("7.打印  8.退出 \n");
	printf("*******************************\n");
}

成品

SeqList.h

#pragma once

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

typedef int SLDataType;

//静态顺序表 -- N太小,可能不够用 N太大,可能浪费空间
//struct SeqList
//{
//	SLDataType a[N];
//	int size;
//	int capa;
//};

//动态顺序表
typedef struct SeqList
{
	SLDataType* a;// 指向数组的指针
	int size;	  // 数据个数
	int capacity;//  容量-空间大小
}SL;

//初始化
void SLInit(SL* ps);

//头插
void SLPushFront(SL* ps, SLDataType x);

//头删
void SLPopFront(SL* ps);

//尾插
void SLPushBack(SL* ps, SLDataType x);

//尾删
void SLPopBack(SL* ps);

//任意位置插入
void SLInsert(SL* ps,int pos, SLDataType x);

//任意位置删除
void SLErase(SL* ps, int pos);

//打印
void SLPrint(SL* ps);

//动态增容
void SLCheckCapacity(SL* ps);

//释放内存
void SLDestory(SL* ps);

//查找
int SLFind(SL* ps, SLDataType x);

//修改
void SLModify(SL* ps, int pos, SLDataType x);

SeqList.c

#include "SeqList.h"

void SLInit(SL* ps)
{
	ps->a = NULL;
	ps->size = ps->capacity = 0;
}

//打印
void SLPrint(SL* ps)
{
	assert(ps!=NULL);

	for (int i = 0;i < ps->size;i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

//动态增容
void SLCheckCapacity(SL* ps)
{
	assert(ps != NULL);

	//检查容量空间,满了扩容
	if (ps->capacity == ps->size)
	{
		int newCapacity = 0;
		if (ps->capacity == 0)
			newCapacity = ps->capacity = 4;
		else
			newCapacity = ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			//exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
	}
}

//任意位置插入 (插入数据都要防止越界)
void SLInsert(SL* ps, int pos, SLDataType 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 SLErase(SL* ps, int pos)
{
	assert(pos >= 0 && pos < ps->size);
	
	int begin = pos;
	while (begin<ps->size)
	{
		ps->a[begin] = ps->a[begin + 1];
		++begin;
	}
	ps->size--;
}

//尾插
void SLPushBack(SL* ps, SLDataType x)
{
	SLInsert(ps, ps->size, x);
}

//头插
void SLPushFront(SL* ps, SLDataType x)
{
	SLInsert(ps, 0, x);
}

//尾删
void SLPopBack(SL* ps)
{
	SLErase(ps, ps->size - 1);
}

//头删
void SLPopFront(SL* ps)
{
	SLErase(ps, 0);
}

//查找
int SLFind(SL* ps, SLDataType x)
{
	for (int i = 0; i <ps->size; i++)
	{
		if (ps->a[i] == x)
			return i;
	}
	return -1;//没找到
}

//修改
void SLModify(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);

	ps->a[pos] = x;
}

//释放内存
void SLDestory(SL* ps)
{
	assert(ps != NULL);

	if (ps->a)
	{
		free(ps->a);
		ps->a = NULL;
		ps->capacity = ps->size = 0;
	}
}

Test.c:

#include "SeqList.h"
void menu()
{
	printf("*******************************\n");
	printf("1.头插  2.尾插	     3.查找 \n");
	printf("4.删除  5.连续删除   6.修改  \n");
	printf("7.打印  8.退出 \n");
	printf("*******************************\n");
}

int main()
{
	//初始化
	SL sl;
	SLInit(&sl);

	int option = -1;
	int x,y,z,f;
	do
	{
		menu();
		scanf("%d", &option);
		int val, pos;
		switch (option)
		{
		case 1:
			printf("请输入要头插的数据,以0结束:>");
			scanf("%d", &val);
			while (val!=0)
			{
				SLPushFront(&sl, val);
				scanf("%d",&val);
			}
			break;
		case 2:
			printf("请输入要尾插的数据,以0结束:>");
			scanf("%d", &val);
			while (val != 0)
			{
				SLPushBack(&sl, val);
				scanf("%d", &val);
			}
			break;
		case 3:
			printf("请输入要查找的数字:>");
			scanf("%d", &y);
			pos = SLFind(&sl, y);
			if (pos != -1)
			{
				printf("找到了%d\n", y);
			}
			else
				printf("没有找到%d\n", y);
			SLPrint(&sl);
			break;
		case 4:
			printf("请输入你要删除的值:>");
			scanf("%d", &x);
			int pos = SLFind(&sl, x);
			if (pos != -1)
			{
				SLErase(&sl, pos);
			}
			else
				printf("没有找到%d\n", x);
			SLPrint(&sl);
			break;
		case 5:
			printf("请输入你要删除的值,并删除所有与之相同的值:>");
			scanf("%d", &f);
			pos = SLFind(&sl, f);
			if (pos != -1)
			{
				while (pos != -1)
				{
					SLErase(&sl, pos);
					pos = SLFind(&sl, f);
				}
			}
			else
				printf("没有找到要删除的值%d\n", f);
			break;
		case 6:
			printf("请输入你要修改的值和修改后的值:>");
			scanf("%d %d", &y, &z);
			pos = SLFind(&sl, y);
			if (pos != -1)
			{
				SLModify(&sl, pos, z);
			}
			else
				printf("没有找到%d\n", y);
			SLPrint(&sl);
			break;
		case 7:
			SLPrint(&sl);
			break;
		case 8:
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (option!=8);
	printf("退出成功\n");
	//释放内存
	SLDestory(&sl);

	return 0;
}

到这里就结束啦,创作不易,求求点个赞啦╰(°▽°)╯

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

npm常用命令 + 前端常用的包管理工具 以及 npm淘宝镜像配置等

npm常用命令 前端常用的包管理工具 以及 npm淘宝镜像配置等 1. 前言1.1 NodeJs的下载安装1.2 windows上1.3 常用包管理工具 2. npm2.1 npm 的安装2.2 npm初始化包2.3 npm 安装、卸载包2.3.1 非全局安装2.3.1.1 单个包的安装2.3.1.1.1 默认版本安装2.3.1.1.2 指定版本安装 2.3.…

Matlab图像处理运算方法-点运算

点运算 点运算又称为对比度增强、对比度拉伸或灰度变换&#xff0c;是一种通过图像中的每一个像素值&#xff08;即像素点上的灰度值&#xff09;进行运算的图像处理方式。它将输入图像映射为输出图像&#xff0c;输出图像每个像素点的灰度值仅由对应的输入像素点的灰度值决定…

(四)CUDA应用程序编程接口详解

C语言扩展 CUDA的编程接口是C语言的扩展集&#xff0c;其中主要的是Runtime库&#xff0c;该库分为三个组件&#xff1a;主机组件、设备组件以及公共组件 主机组件&#xff1a;在主机上运行并提供函数来控制和访问一个或多个计算设备 设备组件&#xff1a;设备运行并且提供特…

树莓派4B上安装Gitlab

参考连接&#xff1a; 树莓派上使用 GitLab 搭建专业 Git 服务 | 树莓派实验室 gitlab reconfigure 卡住 ruby_block[wait for redis service socket] action run_芹菜学长的博客-CSDN博客 以及用到了讯飞星火 系统版本信息 1.进入 giblab安装页面gitlab/gitlab-ce - Instal…

Python——提高数据处理效率的迫切需要

提高数据处理效率的迫切需要 本章包括 处理指数级增长的数据所面临的挑战 传统计算架构与最新计算架构的比较 Python在现代数据分析中的作用和不足 提供高效Python计算解决方案的技术 我们一直在以极快的速度从各种来源收集海量数据。无论目前是否有使用价值&#xff0c;…

YOLOv5算法改进(7)— 添加SimAM注意力机制

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。SimAM&#xff08;Similarity-based Attention Mechanism&#xff09;是一种基于相似度的注意力机制&#xff0c;它的原理是通过计算查询向量与每个键向量之间的相似度&#xff0c;从而确定每个键向量对于查询向量的重要性…

搭建开发环境-Mac

概述 上一篇搭建开发环境-WSLUbuntu 记录了WSL 和Ubuntu 下开发环境的搭建。这一篇就说下Mac开发环境的搭建。 就像很多人误以为Mini 是专为女孩子设计的高颜值车&#xff0c;其实是一辆极其hardcore 的拉力车一样。 很多人都被Mac 那高颜值蒙蔽了&#xff0c;其实这是一台生产…

为DOSBOX增加外部串口功能

如果DOSBOX内部串口与宿主机串口通互通&#xff0c;那DOSBOX中各种语言开发的程序均与外界有了串行通信功能&#xff0c;MODBUS with DOSBOX也就实现了。下面的操作&#xff0c;为DOSBOX打开通往外部的串行通信接口。 1. 在宿主机上创建虚拟串口对。方法是安装 Free 的 com0co…

【NLP的python库(01/4) 】: NLTK

一、说明 NLTK是一个复杂的库。自 2009 年以来不断发展&#xff0c;它支持所有经典的 NLP 任务&#xff0c;从标记化、词干提取、词性标记&#xff0c;包括语义索引和依赖关系解析。它还具有一组丰富的附加功能&#xff0c;例如内置语料库&#xff0c;NLP任务的不同模型以及与S…

【LeetCode75】第三十七题 二叉树中的最长交错路径

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 给我们一棵二叉树&#xff0c;问我们在这棵树里能找到的最长交错路径。最长交错路径就是在二叉树里一左一右一左一右这样走&#xff0c;最…

Golang Gorm 高级查询之where + find

插入测试数据 package mainimport ("fmt""gorm.io/driver/mysql""gorm.io/gorm" )type Student struct {ID int64Name string gorm:"size:6"Age intEmail *string }func (*Student) TableName() string {return "student&q…

自动化测试概况和认知

前言 书中说测试架构师掌握自动化测试的目的不是设计自动化架构或是具体来部署自动化&#xff0c;而是用好自动化。其实这一点我觉得对涉及自动化测试的人员都是通用的&#xff0c;只是偏重点不同。 往往自动化测试前期投入成本可能会大一点&#xff0c;比如框架设计、技术选…

神经网络的工作原理

目录 神经网络的介绍 神经网络的组成 神经网络的工作原理 Numpy 实现神经元 Numpy 实现前向传播 Numpy 实现一个可学习的神经网络 神经网络的介绍 神经网络受人类大脑启发的算法。简单来说&#xff0c;当你睁开眼睛时&#xff0c;你看到的物体叫做数据&#xff0c;再由你…

【论文笔记】Planning and Decision-Making for Autonomous Vehicles

文章目录 Summary1. INTRODUCTION2. MOTION PLANNING AND CONTROL2.1. Vehicle Dynamics and Control2.2. Parallel Autonomy2.3. Motion Planning for Autonomous Vehicles 3. INTEGRATED PERCEPTION AND PLANNING3.1. From Classical Perception to Current Challenges in Ne…

一篇读懂辐射检测仪应用解决方案技术(附方案选型及原理分析)

关于常见的辐射检测仪应用方案&#xff1a; 辐射检测仪是用于测量环境中辐射水平的设备&#xff0c;常用于核电站、医疗机构、实验室和核辐射工作场所等地方。以下是一种可能的辐射检测仪应用方案&#xff1a; 实时监测&#xff1a;辐射检测仪可以实时监测环境中的辐射水平&am…

NIST测试包的可能问题

其实,大多数出现的问题,可能或多或少都可能跟数据集的大小相关。 如: Random Excursions Variant,在测试结果中,可能显示为横线,打开原始报告,可以看到: WARNING: TEST NOT APPLICABLE. THERE ARE AN INSUFFICIENT NUMBER INSUFFICIENT NUMBER OF C…

【C++ 学习 ⑰】- 继承(下)

目录 一、派生类的默认成员函数 二、继承与友元 三、继承与静态成员 四、复杂的菱形继承及菱形虚拟继承 五、继承和组合 一、派生类的默认成员函数 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认构造函数&#xff0c;那么必须在派生…

spring框架:简介+依赖注入

目录 一、spring简介 二、创建项目 三、spring创建对象 四、SpringBean管理 1.注入实现-XML 2.注入实现-注解 一、spring简介 spring诞生与2003年&#xff0c;是一个轻量级的、IOC( Inversion Of Control 控制反转)和AOP(Aspect Oriented Programming 面向切面编程)的jav…

linux挂载内网镜像源文件,支持yum安装

cd /etc/yum.repos.d/vim kylin_aarch64.repo 重建yum缓存 yum clean allyum makecacheyum repolist

【vue】实现高性能虚拟滚动的Vue代码解析

在前端开发中&#xff0c;当需要展示大量数据时&#xff0c;如何保持页面的流畅性是一个挑战。传统的滚动方式会将所有数据一次性渲染到页面&#xff0c;这可能导致页面加载缓慢甚至崩溃。而虚拟滚动技术能够解决这个问题&#xff0c;它只渲染可视区域内的数据&#xff0c;从而…