【数据结构初阶】--- 顺序表

news2024/11/17 10:55:27

顺序表,好像学C语言时从来没听过,实际上就是给数组穿了层衣服,本质是一模一样的。
这里的顺序表实际是定义了一个结构体,设计各种函数来实现它的功能,比如说数组中的增删改查插入,这些基本操作其实平时就会在使用数组的时候用到。
那么,今天就带你深度剖析线性表(数组)的底层原理
这节知识只是个开胃菜,但也属于要必备的知识,所以各位同学不要松懈哦

引入

顺序表有静态的也有动态的

  • 静态顺序表,静,无非就是初始化这个静态表后,这个静态表的空间就不能变化了,例如:
 struct SeqList
* {
* 		int arr[20];
* 	    int size;
* };
  • 当初始化线性表后,这个数组存的数据就不能更改了
  • 那有人会想,我用个宏常量替换数组的大小,到时后想扩大或缩小,更改宏常量就行了
    #define N 20 int arr[N];
  • 这个想法不错,但也只能在线性表初始化之前更改,如果你初始化后,你正在使用线性表的功能时,内存不够用了怎么办

那么,接下来我所编写的代码就是动态顺序表,当你不够用内存的时候,会自动给你开辟,当看到这里,如果前面【C语言进阶】— 动态内存管理,你看过,那接下来的知识易如反掌,甚至不用看我对代码的注释就可以清楚,没看过的朋友,强烈推荐,很重要!!!数据结构会经常用到这篇的知识


1. 顺序表在内存中的存储方式

是一块连续的内存

在这里插入图片描述

2. 顺序表的定义

这里是用结构体定义了一个顺序表类型

typedef int SeqListType;//因为顺序表中存储的数据不见得都是int型,也可能是别的类型数据,所以,想存放别的数据时只需把int换成你想存的数据的类型

//动态顺序表
typedef struct SeqList
{
	SeqListType* data;//指针指向的是SeqListType这个类型的数据
	int size;//记录当前有效储存数据的个数
	int capacity;//data开辟数组空间的容量
}SeqList;

初始化、销毁、打印功能

//初始化顺序表(开辟空间,赋初值)
void SLInit(SeqList* ps)
{
	//开辟一块大小为sizeof(SeqListType) * 4的内存,并把首地址传给指针ps->data
	ps->data = (SeqListType*)malloc(sizeof(SeqListType) * 4);
	if (ps->data == NULL)
	{
		perror("malloc");
		return;
	}
	ps->size = 0;
	ps->capacity = 4;
}
//销毁空间,因为是在堆区开辟的数据,用完要用free函数释放
void SLDestory(SeqList* ps)
{
	free(ps->data);
	ps->data = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

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

3. 顺序表的功能实现

扩容功能,先跳过看头插法

//检查容量,不足顺便扩容
void CheckCapacity(SeqList* ps)
{
	if (ps->capacity == ps->size)
	{
		//用realloc给ps->data开辟一个原来2倍的空间
		SeqListType* new_ps = (SeqListType*)realloc(ps->data,sizeof(SeqListType)*ps->capacity * 2);
		if (new_ps != NULL)//判断返是否开辟成功,若为NULL则开辟失败
		{
			ps->data = new_ps;
			new_ps = NULL;
			ps->capacity *= 2;
		}
		else
		{
			perror("realloc");
			return;
		}
	}
}

3.1 头插法、尾插法

//头插法
void SLPushFront(SeqList* ps, SeqListType x)
{
	//检查目前容量,不足就扩容
	CheckCapacity(ps);

	for (int i = ps->size - 1; i >= 0; i--)
	{
		ps->data[i + 1] = ps->data[i];
	}

	ps->data[0] = x;
	ps->size++;
}

//尾插法
void SLPushBack(SeqList* ps, SeqListType x)
{
	CheckCapacity(ps);

	ps->data[ps->size] = x;
	ps->size++;
}

3.2 头删法、尾删法

//头删法
void SLPopFront(SeqList* ps)
{
	//这里断言的原因是,如果ps是空指针,for循环条件判断就会访问空指针
	assert(ps != NULL);
//*****************当size==0时,也会执行ps->size--,后续可能会造成越界访问***************
	assert(ps->size > 0);
	for (int i = 1; i < ps->size; i++)
	{
		ps->data[i - 1] = ps->data[i];
	}
	ps->size--;
}
//尾删法
void SLPopBack(SeqList* ps)
{
	assert(ps != NULL);
	assert(ps->size > 0);
	ps->size--;
}

3.3 给指定位置插入数据

void SLInsert(SeqList* ps, int position, SeqListType x)
{
	assert(ps != NULL);
	//判断给的位置是否越界,若越界,程序执行完会告诉你这里出的问题
	assert(position >= 1 && position <= ps->size);

	CheckCapacity(ps);

	for (int i = ps->size - 1; i >= position - 1; i--)
	{
		ps->data[i + 1] = ps->data[i];
	}
	ps->data[position - 1] = x;
	ps->size++;
}

3.4 指定位置删除数据

void SLErase(SeqList* ps, int position)
{
	assert(ps != NULL);
	assert(position >= 1 && position <= ps->size);
	assert(ps->size > 0);

	for (int i = position; i < ps->size; i++)
	{
		ps->data[i - 1] = ps->data[i];
	}
	ps->size--;
}

3.5 查找指定数据

int SLFind(SeqList* ps, SeqListType x)
{
	assert(ps != NULL);

	for (int i = 0; i < ps->size; i++)
	{
		if (ps->data[i] == x)
			return i;
	}
	return -1;
}

4.顺序表的优缺点

4.1 优点

因为它跟数组一样可以进行下表访问,所以当你要查询数据时时间复杂度为O(1),是常数级,访问速度很快很方便,这与它的内存是一片连续的内存密切相关,因为当你访问数组下标为i的数据时,arr[i]本质是*(arr+i),首元素地址+i解引用

4.3 缺点

也正因它的内存是连续的,所以当你要删除、插入数据时必须要移动后面的数据,时间复杂度是O(N)级别的。

5 完整代码

5.1 头文件 SeqList.h 中的代码

#pragma once

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

typedef int SeqListType;

//动态顺序表
typedef struct SeqList
{
	SeqListType* data;
	int size;
	int capacity;
}SeqList;

//初始化顺序表
void SLInit(SeqList* ps);
//销毁顺序表
void SLDestory(SeqList* ps);
//打印顺序表
void SLPrint(SeqList* ps);
//头插法插入数据
void SLPushFront(SeqList* ps, SeqListType x);
//尾插法
void SLPushBack(SeqList* ps, SeqListType x);
//头删法
void SLPopFront(SeqList* ps);
//尾删法
void SLPopBack(SeqList* ps);
//指定位置插入数据
void SLInsert(SeqList* ps, int position, SeqListType x);
//指定位置删除
void SLErase(SeqList* ps, int position);
//查找数据
int SLFind(SeqList* ps, SeqListType x);

5.2 函数实现的文件 SeqList.c

#define	_CRT_SECURE_NO_WARNINGS

#include"SeqList.h"

void SLInit(SeqList* ps)
{
	ps->data = (SeqListType*)malloc(sizeof(SeqListType) * 4);
	if (ps->data == NULL)
	{
		perror("malloc");
		return;
	}
	ps->size = 0;
	ps->capacity = 4;
}

void SLDestory(SeqList* ps)
{
	free(ps->data);
	ps->data = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

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

//检查容量,不足顺便扩容
void CheckCapacity(SeqList* ps)
{
	if (ps->capacity == ps->size)
	{
		//用realloc给ps->data开辟一个原来2倍的空间
		SeqListType* new_ps = (SeqListType*)realloc(ps->data,sizeof(SeqListType)*ps->capacity * 2);
		if (new_ps != NULL)
		{
			ps->data = new_ps;
			new_ps = NULL;
			ps->capacity *= 2;
		}
		else
		{
			perror("realloc");
			return;
		}
	}
}

void SLPushFront(SeqList* ps, SeqListType x)
{
	//检查目前容量,不足就扩容
	CheckCapacity(ps);

	for (int i = ps->size - 1; i >= 0; i--)
	{
		ps->data[i + 1] = ps->data[i];
	}

	ps->data[0] = x;
	ps->size++;


}
//尾插法
void SLPushBack(SeqList* ps, SeqListType x)
{
	CheckCapacity(ps);

	ps->data[ps->size] = x;
	ps->size++;
}

void SLPopFront(SeqList* ps)
{
	//这里断言的原因是,如果ps是空指针,for循环条件判断就会访问空指针
	assert(ps != NULL);
//*****************当size==0时,也会执行ps->size--,后续可能会造成越界访问***************
	assert(ps->size > 0);
	for (int i = 1; i < ps->size; i++)
	{
		ps->data[i - 1] = ps->data[i];
	}
	ps->size--;
}
//尾删法
void SLPopBack(SeqList* ps)
{
	assert(ps != NULL);
	assert(ps->size > 0);
	ps->size--;
}

void SLInsert(SeqList* ps, int position, SeqListType x)
{
	assert(ps != NULL);
	assert(position >= 1 && position <= ps->size);

	CheckCapacity(ps);

	for (int i = ps->size - 1; i >= position - 1; i--)
	{
		ps->data[i + 1] = ps->data[i];
	}
	ps->data[position - 1] = x;
	ps->size++;
}

void SLErase(SeqList* ps, int position)
{
	assert(ps != NULL);
	assert(position >= 1 && position <= ps->size);
	assert(ps->size > 0);

	for (int i = position; i < ps->size; i++)
	{
		ps->data[i - 1] = ps->data[i];
	}
	ps->size--;
}

int SLFind(SeqList* ps, SeqListType x)
{
	assert(ps != NULL);

	for (int i = 0; i < ps->size; i++)
	{
		if (ps->data[i] == x)
			return i;
	}
	return -1;
}

这两套代码不能直接运行,主函数int main(){}中的内容自己写,就剩调用函数了,让自己动动手敲几行代码吧,好的程序员一定是需要实践的,尽管顺序表这一节相对不难,但里面有些边界值的判断,只有你亲手敲代码的时候才能真正进入思考,加油各位!

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

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

相关文章

【YOLOV8】4.图片分类-训练自己的数据集

Yolo8出来一段时间了,包含了目标检测、实例分割、人体姿态预测、旋转目标检测、图像分类等功能,所以想花点时间总结记录一下这几个功能的使用方法和自定义数据集需要注意的一些问题,本篇是第四篇,图像分类功能,自定义数据集的训练。 YOLO(You Only Look Once)是一种流行的…

拥抱生态农业,享受绿色生活

随着人们对健康生活的追求日益增强&#xff0c;生态农业逐渐成为人们关注的焦点。我们深知生态农业对于保护生态环境、提高农产品品质的重要性&#xff0c;因此&#xff0c;我们积极推广生态农业理念&#xff0c;让更多的人了解并参与到生态农业的实践中来。 生态农业的蓝总说&…

ALSA 用例配置

ALSA 用例配置。参考 ALSA 用例配置 来了解更详细信息。 ALSA 用例配置 用例配置文件使用 配置文件 语法来定义静态配置树。该树在运行时根据配置树中的条件和动态变量进行评估&#xff08;修改&#xff09;。使用 用例接口 API 解析结果并将其导出到应用程序。 配置目录和主…

苹果手机618大降价重登销量榜首 红米K70pro为何成京东618国产手机之光

今天的618已经好几天了&#xff0c;很多买有机的已经下单&#xff0c;不出意外苹果15系列手机仍然是最卖座的手机&#xff0c;大家虽然口号喊得很响身体却是诚实的。但令人感到意外的是&#xff0c;今年618国产手机的第一把交椅确实红米K70系列&#xff0c;说好的支持华为呢&am…

给孩子的端午节礼物:最新初中数学思维导图大合集+衡水高考学霸笔记,可下载打印!

大家好哇&#xff01;端午节到了&#xff0c;阿星给家里有孩子的伙伴们一份礼物哦&#xff01;今天给大家带来一个超级实用的学习神器——思维导图法&#xff0c;最新版的初中数学思维导图大合集&#xff01; 这可不是我吹哦&#xff0c;连哈佛、剑桥大学都在用的高级学习方法…

常见硬件工程师面试题(一)

大家好&#xff0c;我是山羊君Goat。 对于硬件工程师&#xff0c;学习的东西主要和电路硬件相关&#xff0c;所以在硬件工程师的面试中&#xff0c;对于经验是十分看重的&#xff0c;像PCB设计&#xff0c;电路设计原理&#xff0c;模拟电路&#xff0c;数字电路等等相关的知识…

webman中创建udp服务

webman是workerman的web开发框架 可以很容易的开启udp服务 tcp建议使用gatewayworker webman GatewayWorker插件 创建udp服务: config/process.php中加入: return [// File update detection and automatic reloadmonitor > [ ...........], udp > [handler > p…

转速传感器介绍

一、概述 RPM&#xff08;Revolutions Per Minute&#xff09;转速传感器是一种用于测量旋转机械设备转速的传感器。它可以检测旋转部件上的特定位置标记&#xff08;如齿轮、凸起或磁铁&#xff09;&#xff0c;并根据这些标记的通过频率来计算转速。发电额定频率是50hz和60z…

链表题目练习----重排链表

这道题会联系到前面写的一篇文章----快慢指针相关经典问题。 重排链表 指针法 这道题乍一看&#xff0c;好像有点难处理&#xff0c;但如果仔细观察就会发现&#xff0c;这道题是查找中间节点反转链表链表的合并问题&#xff0c;具体细节有些不同&#xff0c;这个在反装中间链…

Apache Doris 基础 -- 数据表设计(表索引)

1、索引概述 索引用于帮助快速过滤或搜索数据。目前&#xff0c;Doris支持两种类型的索引:内置智能索引和用户创建的二级索引。 内置智能索引 排序键和前缀索引:Apache Doris基于排序键以有序的方式存储数据。它为每1024行数据创建一个前缀索引。索引中的键是当前1024行组的…

国产主流软硬件厂商生态分析

国产领域主流厂商汇总 信创&#xff0c;即信息技术应用创新&#xff0c;由“信息技术应用创新工作委员会”于2016年3月4日发起&#xff0c;是专注于软硬件关键技术研发、应用与服务的非营利性组织。作为科技自强的关键力量&#xff0c;信创在我国信息化建设中占据核心地位&…

小白必学!场外期权的交易模式

场外期权的交易模式 随着金融市场的深化与创新&#xff0c;场外期权交易作为一种灵活多样的金融衍生品交易方式&#xff0c;正逐渐成为投资者关注的焦点。场外期权&#xff0c;顾名思义&#xff0c;是在非交易所市场进行的期权交易&#xff0c;与交易所期权有着显著的区别。那…

IDEA 中设置 jdk 的版本

本文介绍一下 IDEA 中设置 jdk 版本的步骤。 一共有三处需要配置。 第一处 File --> Project Structure Project 和 Modules 下都需要指定一下。 第二处 File --> Settings 第三处 运行时的配置

【C语言进阶】--- 指针详解 3.0

接下来进入指针的进阶部分&#xff0c;准备好大脑 补充&#xff1a;&#xff08;重点&#xff09; 数组名是数组首元素地址 数组首元素地址和数组地址&#xff0c;值相同&#xff0c;但本质不同&#xff0c; 区别在于二者的类型不相同 比如数组int arr[10]; 数组首元素地址的类…

罗马仕、西圣、绿联充电宝哪个牌子好?热销充电宝实测对比!

在这个快节奏的时代&#xff0c;智能手机已成为我们日常生活中不可或缺的一部分&#xff0c;但电量焦虑也随之而来。无论是忙碌的工作日&#xff0c;还是休闲的周末出行&#xff0c;一款可靠、高效的充电宝成为了许多人的随身必备。在市场上众多充电宝品牌中&#xff0c;罗马仕…

题解web

1.[LitCTF 2023]Follow me and hack me 1&#xff09;进入题目环境&#xff0c;提示get传参&#xff0c;post传参 2&#xff09;看看源码&#xff0c;也没啥 3&#xff09;直接用hackbar&#xff0c;传入对应参数即可得到FLAG 3&#xff09;但是扫描出来它后端还有东西&#x…

java线程变量共享

在Java中&#xff0c;线程变量共享可以通过几种方式实现&#xff1a; 1.实例变量&#xff1a;如果一个实例变量被多个线程共享&#xff0c;你需要确保适当的同步&#xff0c;以避免竞态条件。你可以使用synchronized关键字或者Lock接口来保护共享变量。 2.静态变量&#xff1a;…

InternLM-XComposer2-4KHD开拓性的4K高清视觉-语言模型

大型视觉-语言模型&#xff08;LVLM&#xff09;在图像字幕和视觉问答&#xff08;VQA&#xff09;等任务中表现出色。然而&#xff0c;受限于分辨率&#xff0c;这些模型在处理包含细微视觉内容的图像时面临挑战。 分辨率的限制严重阻碍了模型处理含有丰富细节的图像的能力。…

高级文件操作

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 Python内置的os模块除了可以对目录进行操作&#xff0c;还可以对文件进行一些高级操作&#xff0c;具体函数如表4所示。 表4 os模块提供的与文件相…

大模型在信用卡行业的应用探索

2022年11月&#xff0c;OpenAI发布ChatGPT3.5&#xff0c;迅速引起各界广泛关注&#xff0c;引发了人工智能领域新一轮发展热潮。ChatGPT作为一款基于人工智能技术的大语言模型&#xff08;LLMs&#xff09;&#xff0c;在文本生成、对话理解、多领域知识覆盖等方面具有卓越表现…