【算法与数据结构】顺序表

news2024/9/24 11:32:45

顺序表

数据结构 = 结构定义+结构操作

顺序表:结构定义

一个数组,添加额外的几个属性:size, count等
size: 数组有多大
count: 数组中当前存储了多少元素
在这里插入图片描述
顺序表三部分:

  1. 一段连续的存储区:顺序表存储元素的地方
  2. 整型的变量size: 标记顺序表的大小
  3. 整型变量count: 标记顺序表中到底存储了多少了元素

代码

结构定义

typedef struct vector{
	int size, count;
	int *data;   // 一段连续的存储区,存储的是整型元素, data指针指向整型的存储区

}vector;

构造函数
首先开辟顺序表本身的空间p, 然后给p的size赋值,p的count赋值,最后给p的data赋值,p的data指向的是顺序表中那段连续的存储区,存储区的大小是n

vector * getNewVector(int n)
{
	vector *p = (vector *)malloc(sizeof(vector));
	p->size = n;
	p->count = 0;
	p->data = (int*)malloc(sizeof(int)* n);
	return p;
}

析构函数
销毁之前先判断一下,如果对象为空,就直接返回;先销毁顺序表中连续的存储区,再销毁顺序表本身的存储空间

void clear(vector* v)
{
	if (v == NULL) return;
	free(v->data);
	free(v);
	return;
}

顺序表:插入

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
size不变
count+1

代码

插入之前先判断一下,插入位置的合法性以及顺序表的地方是否够用,如果存满了,本次插入不成功,返回0;否则没有存满,则可以将所有元素平行向后移动一位,以逆序遍历向后移动,从最后一位(count - 1)开始遍历,递减到插入位置pos,然后将元素插入。
最后,数据结构是包括性质的,还需要维护性质,即count+1

int insert(vector*v, int pos, int val)
{
    if (pos < 0 || pos > v->count) return 0;
	if (v->size == v->count) return 0;
	for (int i = v->count - 1; i >= pos; i--)
	{
		v->data[i + 1] = v->data[i];
	}
	v->data[pos] = val;
	v->count += 1;
	return 1;
}

顺序表:删除

在这里插入图片描述
在这里插入图片描述
size不变
count-1

代码

判断删除位置的合法性,然后后面的元素平行向前移动一个元素

int erase(vector *v, int pos)
{
	if (pos < 0 || pos >= v->count) return 0;
	for (int i = pos + 1; i < v->count; i++)
	{
		v->data[i - 1] = v->data[i];
	}

	v->count -= 1;
	return 1;
}

代码演示

用一组随机操作,对顺序表进行测试
可以在4范围内随机: 0, 1, 2表示插入, 3表示删除
随机插入的位置: 随机数 % (count +2), 随机出一些非法位置,测试程序返回的结果是否正确。
然后进行插入和删除操作
输出格式:第一行是数组的序号,第二行是小横线,第三行元素

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <iostream>


typedef struct vector{
	int size, count;
	int *data;   // 一段连续的存储区,存储的是整型元素, data指针指向整型的存储区

}vector;

vector * getNewVector(int n)
{
	vector *p = (vector *)malloc(sizeof(vector));
	p->size = n;
	p->count = 0;
	p->data = (int*)malloc(sizeof(int)* n);
	return p;
}

int insert(vector*v, int pos, int val)
{
	if (pos < 0 || pos > v->count) return 0;
	if (v->size == v->count) return 0;
	for (int i = v->count - 1; i >= pos; i--)
	{
		v->data[i + 1] = v->data[i];
	}
	v->data[pos] = val;
	v->count += 1;
	return 1;
}

int erase(vector *v, int pos)
{
	if (pos < 0 || pos >= v->count) return 0;
	for (int i = pos + 1; i < v->count; i++)
	{
		v->data[i - 1] = v->data[i];
	}

	v->count -= 1;
	return 1;
}


void clear(vector* v)
{
	if (v == NULL) return;
	free(v->data);
	free(v);
	return;
}

void output_vector(vector  *v)
{
	
	int len = 0;
	for (int i = 0; i < v->size; i++)
	{
		len += printf("%3d", i);   // 控制整型输出的宽度, 累加输出的长度
	}
	printf("\n");
	for (int i = 0; i < len; i++) printf("-");   // 注意输出的长度为len与上面对齐
	printf("\n");
	for (int i = 0; i < v->count; i++)    // 注意输出的长度为count
	{
		printf("%3d", v->data[i]);
	}
	printf("\n");
	printf("\n\n");
	return;
}


int main()
{
	srand(time(0));
    #define MAX_OP 20
	vector *v = getNewVector(MAX_OP);
	for (int i = 0; i < MAX_OP; i++)
	{
		int op = rand() % 4, pos, val;
		switch (op)
		{
		case 0:
		case 1:
		case 2:
			pos = rand() % (v->count + 2);
			val = rand() % 100;
			printf("insert %d at %d to vector = %d\n", val, pos, insert(v, pos, val));
			break;
		case 3:
			pos = rand() % (v->count + 2);
			printf("erase item at %d in vector = %d\n", pos, erase(v, pos));
			break;

		}
		output_vector(v);

	}
	clear(v);

	std::cin.get();
	return 0;

}

输出:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

顺序表:扩容操作

当进行插入操作的时候,发现顺序表已经满了,可以自动扩容,扩容后就可以插入成功。
假设现在已经有了扩容成功了,什么情况下才返回插入不成功? 答案:顺序表满了,并且扩容操作不成功,才返回0
还是跟前面一样,先判断一下传进来的顺序表的指针合不合法,判断指针是否指向空地址。
扩容策略: 二倍扩容法,如果原来顺序表的大小是size,尝试将size的顺序表扩容到2*size的大小,扩容的是顺序表的data区

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <iostream>


typedef struct vector{
	int size, count;
	int *data;   // 一段连续的存储区,存储的是整型元素, data指针指向整型的存储区

}vector;

vector * getNewVector(int n)
{
	vector *p = (vector *)malloc(sizeof(vector));
	p->size = n;
	p->count = 0;
	p->data = (int*)malloc(sizeof(int)* n);
	return p;
}

int expand(vector *v)
{
	if (v == NULL) return 0;
	printf("expand v from %d to %d\n", v->size, 2 * v->size );   // 输出扩容提示信息
	v->data = (int *)realloc(v->data, sizeof(int) * 2 * v->size);  // realloc(重新分配存储区的地址,希望重新分配的内存大小)
	v->size *= 2;   // 更新扩容后的size,2倍
	return 1;
}


int insert(vector*v, int pos, int val)
{
	if (pos < 0 || pos > v->count) return 0;
	if (v->size == v->count && !expand(v)) return 0;  // 如果满了且扩容失败,返回0
	for (int i = v->count - 1; i >= pos; i--)
	{
		v->data[i + 1] = v->data[i];
	}
	v->data[pos] = val;
	v->count += 1;
	return 1;
}

int erase(vector *v, int pos)
{
	if (pos < 0 || pos >= v->count) return 0;
	for (int i = pos + 1; i < v->count; i++)
	{
		v->data[i - 1] = v->data[i];
	}

	v->count -= 1;
	return 1;
}


void clear(vector* v)
{
	if (v == NULL) return;
	free(v->data);
	free(v);
	return;
}

void output_vector(vector  *v)
{
	
	int len = 0;
	for (int i = 0; i < v->size; i++)
	{
		len += printf("%3d", i);   // 控制整型输出的宽度, 累加输出的长度
	}
	printf("\n");
	for (int i = 0; i < len; i++) printf("-");   // 注意输出的长度为len与上面对齐
	printf("\n");
	for (int i = 0; i < v->count; i++)    // 注意输出的长度为count
	{
		printf("%3d", v->data[i]);
	}
	printf("\n");
	printf("\n\n");
	return;
}


int main()
{
	srand(time(0));
    #define MAX_OP 20
	vector *v = getNewVector(2);        // 
	for (int i = 0; i < MAX_OP; i++)
	{
		int op = rand() % 4, pos, val, ret;
		switch (op)
		{
		case 0:    // 注意case要从0开始
		case 1:
		case 2:
			pos = rand() % (v->count + 2);
			val = rand() % 100;
			ret = insert(v, pos, val);
			printf("insert %d at %d to vector = %d\n", val, pos, ret);
			break;
		case 3:
			pos = rand() % (v->count + 2);
			ret = erase(v, pos);
			printf("erase item at %d in vector = %d\n", pos, ret);
			break;

		}
		output_vector(v);

	}
	clear(v);

	std::cin.get();
	return 0;

}

输出:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

进一步分析

其实在重新分配内存(realloc)的时候有bug,realloc的策略:

  1. realloc会在原内存地址上试着向后去进行延展,如果向后扩展成功了,那么返回的地址就会和原内存地址是一样的,但是大小会增加一倍
  2. 找一片新的内存,把原内存的内容拷贝到新内存里面,并且返回新内存的地址。
  3. 以上两种情况都找不到,就会返回NULL

所以,再返回来看源代码,第一和第二种情况都没有bug,但是第三种情况,即扩容不成功的情况下,realloc会返回一个空地址,空地址会覆盖掉原来存储的地址,这个时候就会发生我们data中原来存储的地址信息丢失了。
解决方案:新定义一个指针,把realloc的返回值赋值给这个新指针,然后先判断是否为空,不是空地址才将新地址赋值给data

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <iostream>


typedef struct vector{
	int size, count;
	int *data;   // 一段连续的存储区,存储的是整型元素, data指针指向整型的存储区

}vector;

vector * getNewVector(int n)
{
	vector *p = (vector *)malloc(sizeof(vector));
	p->size = n;
	p->count = 0;
	p->data = (int*)malloc(sizeof(int)* n);
	return p;
}

int expand(vector *v)
{
	if (v == NULL) return 0;
	printf("expand v from %d to %d\n", v->size, 2 * v->size );   // 输出扩容提示信息
	int *p = (int *)realloc(v->data, sizeof(int)* 2 * v->size);  // realloc(重新分配存储区的地址,希望重新分配的内存大小)
	if (p == NULL) return 0;
	v->data = p;
	v->size *= 2;   // 更新扩容后的size,2倍
	return 1;
}


int insert(vector*v, int pos, int val)
{
	if (pos < 0 || pos > v->count) return 0;
	if (v->size == v->count && !expand(v)) return 0;  // 如果满了且扩容失败,返回0
	for (int i = v->count - 1; i >= pos; i--)
	{
		v->data[i + 1] = v->data[i];
	}
	v->data[pos] = val;
	v->count += 1;
	return 1;
}

int erase(vector *v, int pos)
{
	if (pos < 0 || pos >= v->count) return 0;
	for (int i = pos + 1; i < v->count; i++)
	{
		v->data[i - 1] = v->data[i];
	}

	v->count -= 1;
	return 1;
}


void clear(vector* v)
{
	if (v == NULL) return;
	free(v->data);
	free(v);
	return;
}

void output_vector(vector  *v)
{
	
	int len = 0;
	for (int i = 0; i < v->size; i++)
	{
		len += printf("%3d", i);   // 控制整型输出的宽度, 累加输出的长度
	}
	printf("\n");
	for (int i = 0; i < len; i++) printf("-");   // 注意输出的长度为len与上面对齐
	printf("\n");
	for (int i = 0; i < v->count; i++)    // 注意输出的长度为count
	{
		printf("%3d", v->data[i]);
	}
	printf("\n");
	printf("\n\n");
	return;
}


int main()
{
	srand(time(0));
    #define MAX_OP 20
	vector *v = getNewVector(2);        // 
	for (int i = 0; i < MAX_OP; i++)
	{
		int op = rand() % 4, pos, val, ret;
		switch (op)
		{
		case 0:    // 注意case要从0开始
		case 1:
		case 2:
			pos = rand() % (v->count + 2);
			val = rand() % 100;
			ret = insert(v, pos, val);
			printf("insert %d at %d to vector = %d\n", val, pos, ret);
			break;
		case 3:
			pos = rand() % (v->count + 2);
			ret = erase(v, pos);
			printf("erase item at %d in vector = %d\n", pos, ret);
			break;

		}
		output_vector(v);

	}
	clear(v);

	std::cin.get();
	return 0;

}

总结

相关数据结构到底支持什么操作,是一个不确定性的,非常灵活的,是自己设计的。

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

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

相关文章

【Linux】Linux安装Java环境(OracleJDK)

文章目录 前言第一步&#xff0c;到官网下载jdk1.8第二步&#xff0c;下载下来上传到/opt目录下&#xff0c;并且解压第三步&#xff0c;解压之后配置环境变量&#xff1a;第四步&#xff0c;刷新配置文件第五步&#xff0c;查看版本 前言 linux环境为CentOS7.8 版本。 上期跟…

shell教程

面试题&#xff1a; 1.Shell中单引号和双引号区别 1)单引号不取变量值 2)双引号取变量值 3)反引号&#xff0c;执行引号中命令 4)双引号内部嵌套单引号&#xff0c;取出变量值 5)单引号内部嵌套双引号&#xff0c;不取出变量值 一、shell脚本 1.shell脚本概…

ChatGLM的搭建过程

本次搭建的是清华大学开源的ChatGLM。源码地址。模型地址。 1、开启BBR加速 如何开启BBR加速可以去看我的这篇文章&#xff0c;Linux开启内核BBR加速。 2、拉取ChatGLM源码和ChatGLM模型 点击这里跳转到源码处。 点击这里跳转到模型下载处。 我这里在下载之前创建了一个目…

与ChatGPT的一次技术对话

文章目录 前言 - 向引领前沿技术的伟大工作者致敬提问&#xff1a;请给我推荐几个最先进的目标检测AI模型提问&#xff1a;YOLOv4是什么原理&#xff0c;有什么创新提问&#xff1a;请问你知道yolov5吗提问&#xff1a; 那yolov5又有什么创新呢提问&#xff1a;你刚刚的回答正确…

SwiftUI 4.0 新 LabeledContent 视图帮您解决所有对齐烦恼

概览 在用 SwiftUI Form 设计 App 界面时&#xff0c;最头疼的就是内部视图对齐的问题了。好不容易适配了 iOS 中的布局&#xff0c;到了 iPadOS 或 MacOS 上却变得一团糟。 有没有一劳永逸&#xff0c;简单方便的办法呢&#xff1f; 如上图所示&#xff1a;我们利用 SwiftUI…

3. SQL底层执行原理详解

一条SQL在MySQL中是如何执行的 1. MySQL的内部组件结构1.1 Server层1.2 Store层 2. 连接器3. 分析器3.1 词法分析器原理 4. 优化器5. 执行器6. bin-log归档 本文是按照自己的理解进行笔记总结&#xff0c;如有不正确的地方&#xff0c;还望大佬多多指点纠正&#xff0c;勿喷。 …

基于ASP.NET的Web应用系统架构探讨

摘要&#xff1a;提出了一种基于ASP&#xff0e;NET开发方式的四层架构的Web应用系统构造思想。其基本内容是&#xff1a;将面向对象的UML建模与Web应用系统开发相结合&#xff0c;将整个系统分成适合ASP.NET开发方式的应用表示层、业务逻辑层、数据访问层和数据存储层四层结构…

[CF复盘] Codeforces Round 871 (Div. 4)20230506

[CF复盘] Codeforces Round 871 Div. 4 20230506 总结A. Love Story1. 题目描述2. 思路分析3. 代码实现 B. Blank Space1. 题目描述2. 思路分析3. 代码实现 C. Mr. Perfectly Fine1. 题目描述2. 思路分析3. 代码实现 D. Gold Rush1. 题目描述2. 思路分析3. 代码实现 E. The La…

UNeXt:基于 MLP 的快速医学图像分割网络

UNeXt是约翰霍普金斯大学在2022年发布的论文。它在早期阶段使用卷积&#xff0c;在潜在空间阶段使用 MLP。通过一个标记化的 MLP 块来标记和投影卷积特征&#xff0c;并使用 MLP 对表示进行建模。对输入通道进行移位&#xff0c;可以专注于学习局部依赖性。 UNeXt 基本架构 U…

考研数学高数1-1综合测试-函数及其性质

今天听完强化课之后去做学习包的题&#xff0c;发现没带平板&#xff0c;如果写到纸上&#xff0c;塞到书里又不知道去哪了&#xff0c;所以索性就拿Latex写了&#xff0c;虽然有一点麻烦&#xff0c;但是好在数量不多&#xff0c;就这么写吧。 都是我自己写的过程&#xff0c;…

第三十八章 Unity GUI系统(下)

上一章节我们将了UI的锚点&#xff0c;关于锚点我们只讲了一半&#xff0c;因为锚点并不只是一个点&#xff0c;它还可以是一个矩形。 我们可以将锚点拆开&#xff08;鼠标选中后拖动&#xff09;&#xff0c;也就是将“四瓣雪花”拆成“四瓣”。那么此时锚点就成为一个矩形。我…

00-docker篇: linux系统安装docker操作 (最实用的操作)

目录 1. docker 简介 -> 简易理解: -> docker是否有自己仓库呢 -> docker 是靠什么运行呢 -> 简单说点docker优点 2. linux安装docker ps: 如果是新服务器 请直接看2. 3 -> 2.1: 查看内核版本: -> 2.2 如果有残留docker, 删除指令 -> 2.3 yu…

2023年全国职业院校技能大赛网络建设与运维-网络运维部分

全国职业院校技能大赛 网络建设与运维 五、网络运维 某单位网络拓扑架构如下&#xff0c;交换机连接两台服务器&#xff0c;其中Server1服务器是数字取证服务器&#xff0c;Server2服务器是应急响应服务器&#xff0c;通过交换设备相连&#xff0c;通过路由设备连接到安全设…

Portraiture4最新版滤镜P图一键磨皮插件

今天coco玛奇朵给大家带来了一款ps磨皮插件&#xff0c;超级简单好用。Portraiture 滤镜是一款 Photoshop&#xff0c;Lightroom 和 Aperture 插件&#xff0c;DobeLighttroom 的 Portraiture 消除了选择性掩蔽和逐像素处理的繁琐的手工劳动&#xff0c;以帮助您在肖像修整方面…

如何使用 ChatGPT 来快速编写产品需求文档(PRD)

PRD 生成 ChatGPT 即了解具体的编程知识&#xff0c;也了解编程之前的需求设计过程。因此产品经理也可以使用 ChatGPT 来快速编写PRD(产品需求文档, production requirement documentation)。 根据需求编写 PRD 首先&#xff0c;我们可以尝试把需求交给 ChatGPT&#xff0c;…

模型如何压缩?使用轻量化的模型压缩技术剪枝(pruning)

深度学习模型参数太多&#xff0c;本地服务器部署没有问题&#xff0c;但是如果部署到移动端、边缘端&#xff0c;像手机、树莓派等&#xff0c;它们的性能不能满足&#xff0c;所以我们要压缩模型大小&#xff0c;让他们可以部署到边缘端 模型压缩&#xff1a;使用轻量化的模型…

Redis 布隆过滤器总结

Redis 布隆过滤器总结 适用场景 大数据判断是否存在来实现去重&#xff1a;这就可以实现出上述的去重功能&#xff0c;如果你的服务器内存足够大的话&#xff0c;那么使用 HashMap 可能是一个不错的解决方案&#xff0c;理论上时间复杂度可以达到 O(1) 的级别&#xff0c;但是…

Flutter 开发的那些小细节

Flutter 创建应用的小注意 包名 每当创建一个新的 Flutter 应用时&#xff0c;一些 Flutter IDE 插件会请你输入一个类似 com.example 的包名&#xff0c;包名&#xff08;在 iOS 里叫 Bundle ID&#xff09;一般都是公司域名的反写。如果你的应用打算上架商店&#xff0c;建…

NetApp 7-mode下Autosupport日志的收集

前面介绍过NetApp Cluster mode下autosupport日志的收集方法&#xff0c;最近遇到很多7-mode下客户扔出一个有故障指示灯的照片&#xff0c;然后让你判断问题的case。NetApp没有一个命令能很清晰的把所有的和硬件有关的问题列出来的命令。客户随之就说&#xff0c;要不输入一条…

【操作系统OS】学习笔记第三章 内存管理【哈工大李治军老师】

基于本人观看学习 哈工大李治军老师主讲的操作系统课程 所做的笔记&#xff0c;仅进行交流分享。 特此鸣谢李治军老师&#xff0c;操作系统的神作&#xff01; 如果本篇笔记帮助到了你&#xff0c;还请点赞 关注 支持一下 ♡>&#x16966;<)!! 主页专栏有更多&#xff0…