顺序表的实现和练习

news2024/12/28 18:33:53

杂谈:

有些数据结构(C语言实现)的教材/教程中会使用C++中引用的语法,引用确实在形式上比指针简洁,这样做无非是为了避免后续对二级指针的使用。

我认为既然使用C语言实现数据结构,那么指针就不应该是门槛。增加了引用的C到底是C还是C++呢?如果使用C++完全可以用类实现数据结构,而不是使用兼容C的低级语法。

C语言实现数据结构重要前置知识:指针、结构体、动态内存管理、(递归、函数栈帧...)。

顺序表实现(动态版本)

用C实现顺序表结构(动态版本,支持自动扩容),及相关操作函数:初始化、销毁、打印、插入(任意位置、头插、尾插)、删除(任意位置、头删、尾删)、查找、修改。

注:本文(包括后续数据结构实现)函数命名均采用C++STL的命名风格

定义顺序表及函数声明

在SeqList.h头文件中定义顺序表结构及声明相关函数:

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

typedef int SLDataType; // 顺序表数据类型
typedef struct SeqList  // 顺序表结构
{
	SLDataType* a; // 动态数组,存放数据元素
	int size;      // 元素个数
	int capacity;  // 数组容量
}SL;

void SLInit(SL* psl);   // 顺序表初始化
void SLDestroy(SL* psl);// 销毁顺序表

// 对数据的管理:增删查改 
void SLPrint(SL* psl); // 打印顺序表
void SLPushBack(SL* psl, SLDataType x); // 尾插元素
void SLPushFront(SL* psl, SLDataType x);// 头插元素
void SLPopFront(SL* psl);// 头删
void SLPopBack(SL* psl); // 尾删

// 顺序表查找
int SLFind(SL* ps, SLDataType x);
// 顺序表在pos位置(下标)插入x
void SLInsert(SL* ps, int pos, SLDataType x);
// 顺序表删除pos位置(下标)的值
void SLErase(SL* ps, int pos);
// 顺序表修改pos位置的值
void SLModify(SL* psl, int pos, SLDataType x);

函数定义

在SeqList.c文件中定义顺序表操作函数,为方便演示,所有函数将单独展示:

初始化

#include "SeqList.h"
void SLInit(SL* psl)
{
	assert(psl);// 断言psl是否为空指针

	psl->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);// 初始容量设置为4
	if (psl->a == NULL) //动态申请失败,结束函数
	{
		perror("malloc fail");
		return;
	}

	psl->capacity = 4;
	psl->size = 0;
}

销毁

注:对顺序表指针的销毁应由使用者操作 free(psl); psl = NULL;

void SLDestroy(SL* psl)
{
	assert(psl);

	free(psl->a); // 释放动态数组
	psl->a = NULL;// 指针置空
	psl->size = psl->capacity = 0;// 个数、容量置0
}

打印

void SLPrint(SL* psl)
{
	assert(psl);

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

检查容量

容量不够则扩容2倍

void SLCheckCapacity(SL* psl)
{
	assert(psl);

	if (psl->size == psl->capacity)// 顺序表已满,需要扩容
	{
		SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType) * psl->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		psl->a = tmp;
		psl->capacity *= 2;
	}
}

插入

void SLInsert(SL* psl, int pos, SLDataType x)
{
	assert(psl);

	assert(0 <= pos && pos <= psl->size);// 断言pos是否合法,可在原范围[0,size)和下一位置size插入

	SLCheckCapacity(psl);

	int end = psl->size - 1;
	while (end >= pos) 
	{
		psl->a[end + 1] = psl->a[end];// 元素向后移动一位
		--end;
	}

	psl->a[pos] = x;// pos位置插入x
	psl->size++;
}

删除

void SLErase(SL* psl, int pos)
{
	assert(psl);

	assert(0 <= pos && pos < psl->size);// 只能删除原范围数据[0,size)

	int start = pos + 1;
	while (start < psl->size)
	{
		psl->a[start - 1] = psl->a[start];// 元素向前移动一位
		++start;
	}

	psl->size--;
}

尾插

在顺序表末尾增加一个元素

void SLPushBack(SL* psl, SLDataType x)
{
	assert(psl);

	SLInsert(psl, psl->size, x);
}

头插

在顺序表第一个元素前插入一个元素

void SLPushFront(SL* psl, SLDataType x)
{
	assert(psl);

	SLInsert(psl, 0, x);
}

尾删

删除顺序表最后一个元素

void SLPopBack(SL* psl)
{
	assert(psl);

	SLErase(psl, psl->size - 1);
}

头删

删除顺序表第一个元素

void SLPopFront(SL* psl)
{
	assert(psl);

	SLErase(psl, 0);
}

查找

查找元素第一个位置,返回其下标(未找到返回-1)

int SLFind(SL* psl, SLDataType x)
{
	assert(psl);

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

	return -1;
}

修改

void SLModify(SL* psl, int pos, SLDataType x)
{
	assert(psl);

	assert(0 <= pos && pos < psl->size);

	psl->a[pos] = x;// 修改pos位置数据
}

测试

在test.c文件中定义函数或直接在main函数中测试顺序表功能,建议每实现一部分功能就进行相关测试。如果实现完顺序表的所有操作函数在去测试,不容易发现错误。

1 尾插测试

void TestSeqList1()
{
	SL s;
	SLInit(&s);

	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);

	SLPrint(&s);

	SLDestroy(&s);
}

运行结果:

2 头插测试

void TestSeqList2()
{
	SL s;
	SLInit(&s);

	SLPushFront(&s, 1);
	SLPushFront(&s, 2);
	SLPushFront(&s, 3);
	SLPushFront(&s, 4);
	SLPushFront(&s, 5);

	SLPrint(&s);

	SLDestroy(&s);
}

运行结果:

3 尾删测试

void TestSeqList3()
{
	SL s;
	SLInit(&s);

	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);

	SLPrint(&s);

	SLPopBack(&s);
	SLPopBack(&s);
	SLPopBack(&s);

	SLPrint(&s);

	SLDestroy(&s);
}

运行结果:

4 头删测试

void TestSeqList4()
{
	SL s;
	SLInit(&s);

	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);

	SLPrint(&s);

	SLPopFront(&s);
	SLPopFront(&s);

	SLPrint(&s);

	SLDestroy(&s);
}

运行结果:

5 插入测试

void TestSeqList5()
{
	SL s;
	SLInit(&s);

	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);

	SLPrint(&s);

	SLInsert(&s, 3, 30);
	SLPrint(&s);

	SLDestroy(&s);
}

运行结果:

6 删除测试

void TestSeqList6()
{
	SL s;
	SLInit(&s);

	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);

	SLPrint(&s);

	SLErase(&s, 2);
	SLPrint(&s);

	SLDestroy(&s);
}

运行结果:

7 查找、修改测试

void TestSeqList7()
{
	SL s;
	SLInit(&s);

	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPushBack(&s, 5);

	SLPrint(&s);

	int pos = SLFind(&s, 3);
	if (pos != -1)
	{
		SLErase(&s, pos);
	}
	SLPrint(&s);

	SLDestroy(&s);
}

运行结果:

题目练习(不定时增加)

1.原地移除数组中所有的元素val。

题目链接:移除元素

方法1:

  1. 从前向后遍历数组nums,找到第一个val
  2. 将val后面的元素向前移动一位,即删除该val;numsSize-1
  3. 循环重复上述操作,直到所有val被删除
  4. 返回numSize

时间复杂度:O(n^2) 空间复杂度:O(1)

图解:

代码:

int removeElement(int* nums, int numsSize, int val)
{
    for(int i = 0; i < numsSize; i++) // 遍历数组
    {
        if(nums[i] == val)// 找到val
        {
            for(int j = i;j < numsSize-1; j++)
            {
                nums[j] = nums[j+1];// val后面元素向前移动一位
            }

            i--; // val下一个元素可能也是val,需要重新判断该位置(图解省略了这种情况)
            numsSize--;// 数组个数-1
        }    
    }

    return numsSize;
}

方法2:

  1. 设置变量count用来记录数组nums中val的个数
  2. 遍历数组,对于数组中每个元素,如果该元素=val则count++,否则将该元素向前移动count个位置(因为前面count个val已经被删除了,后面元素需要向前覆盖)
  3. 返回numsSize-count

时间复杂度:O(n) 空间复杂度:O(1)

图解:

代码:

int removeElement(int* nums, int numsSize, int val){
    int count = 0;
    for(int i = 0; i < numsSize; i++)
    {
        if(nums[i] == val)
        {
            count++;
        }
        else
        {
            nums[i-count] = nums[i];
        }
    }


    return numsSize - count;
}

方法3:双指针

  1. 用count记录val个数
  2. 指针i从前向后扫描数组nums,指针j指向nums[0],如果nums[i]不等于val,则赋值给nums[j],j指向下一位置,否则,count+1。
  3. 返回numsSize-count。

时间复杂度:O(n) 空间复杂度:O(1)

图解:

代码:

int removeElement(int* nums, int numsSize, int val){
    int count = 0;
    for (int i = 0, j = 0; i < numsSize; i++)
    {
        if (nums[i] != val)
        {
            nums[j] = nums[i];
            j++;
        }
        else
        {
            count++;
        }
    }

    return numsSize - count;
}

如果本文内容对你有帮助,可以点赞收藏,感谢支持,期待你的关注。

本专栏下篇预告:单链表实现及练习

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

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

相关文章

【动手学深度学习-Pytorch版】序列到序列的学习(包含NLP常用的Mask技巧)

序言 这一节是对于“编码器-解码器”模型的实际应用&#xff0c;编码器和解码器架构可以使用长度可变的序列作为输入&#xff0c;并将其转换为固定形状的隐状态&#xff08;编码器实现&#xff09;。本小节将使用“fra-eng”数据集&#xff08;这也是《动手学习深度学习-Pytor…

[论文分享] How to Better Utilize Code Graphs in Semantic Code Search?

How to Better Utilize Code Graphs in Semantic Code Search? [ESEC/FSE 2022] 语义代码搜索极大地促进了软件的重用&#xff0c;使用户能够找到与用户指定的自然语言查询高度匹配的代码片段。由于代码图(如控制流图和程序依赖图)丰富的表达能力&#xff0c;两种主流的研究工…

【Gradle-9】Gradle插件发布指南

1、前言 不管是在公司内部&#xff0c;还是开源&#xff0c;Gradle插件发布都是一项必备的技能&#xff0c;本文主要介绍本地发布和远端发布两种方式。 2、本地发布 2.1、添加依赖 在plugin>build.gradle文件中&#xff08;插件的项目&#xff09;先依赖一个maven发布的…

分布式搜索引擎Elasticsearch

一、Elasticsearch介绍 1.Elasticsearch产生背景 大数据量的检索NoSql: not only sql,泛指非关系型的数据库Nginx的7层负载均衡和4层负载均衡2.Elasticsearch是什么 一个基于Lucene的分布式搜索和分析引擎,一个开源的高扩展的分布式全文检索引擎 Elasticsearch使用Java开发…

零基础也能制作小说推文视频,输入文案就能制作推文短视频

小说推文视频一直是各类写手们追捧的创作方式之一&#xff0c;而如何制作出优质、吸引人的小说推文视频成了许多人关注的焦点。幸运的是&#xff0c;现在有了一款名为推文视频制作神器&#xff0c;让制作小说推文视频变得轻松简单。 这款小说推文视频神器的功能十分强大&#…

山西电力市场日前价格预测【2023-09-25】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-09-25&#xff09;山西电力市场全天平均日前电价为442.30元/MWh。其中&#xff0c;最高日前电价为720.46元/MWh&#xff0c;预计出现在19: 00。最低日前电价为276.06元/MWh&#xff0c;预计…

AUTOSAR 多核操作系统时序监控系统设计

AUTOSAR 多核操作系统时序监控系统设计 0 引言1 AUTOSAR 介绍1.1 AUTOSAR 诞生1.3 AUTOSAR 架构 2 时序监控系统软硬件介绍2.1 硬件部分2.2 软件部分 3 时序监控系统设计3.1 监控系统整体设计3.2 监控数据获取3.3 监控数据存储3.4 监控数据处理 3.5 还原运行时序5 推动 5G工业互…

3D点云目标检测:Centerformer训练waymo数据集

一、环境准备 项目地址:centerformer 1.0、基础环境 python 3.8.0 torch 1.9.1cu111 waymo-open-dataset-tf-2-6-0 1.4.9 spconv 1.2.1 其余按照requirement.txt里安装就行 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt由于我本人是在…

uniapp、vue实现滑动拼图验证码

uniapp、vue实现滑动拼图验证码 实际开发工作中&#xff0c;在登陆的时候需要短信验证码&#xff0c;但容易引起爬虫行为&#xff0c;需要用到反爬虫验证码&#xff0c;今天介绍一下拼图验证码&#xff0c;解决验证码反爬虫中的滑动验证码反爬虫。滑动拼图验证码是在滑块验证码…

QLineEdit设置数据的输入范围QIntValidator和QDoubleValidator

在日常开发过程中QLineEdit作为输入框&#xff0c;有时要限制输入的内容&#xff0c;比哪&#xff0c;考试分数为0-100&#xff0c;这个时候就使用QIntValidator作为限制范围&#xff0c;而如何输入的是带小数的呢&#xff0c;那么使用QDoubleValidator可以吗&#xff0c;下面请…

ipad触控笔有必要买原装吗?ipad2023手写笔推荐

目前&#xff0c;在无纸教学、无纸办公的大背景下&#xff0c;电容笔得到了广泛的关注。只是&#xff0c;对于这两支电容笔的不同之处&#xff0c;不少人并不是很清楚。其实这两种电容笔都很好区分&#xff0c;第一种是主动电容笔&#xff0c;也就是我们常用的电容式屏幕&#…

安全生产一张图 安全生产三维地理信息平台

一、 建设目标 易图讯科技是一家专业从事大数据、移动互联网、物联网、三维GIS、AI系统研发&#xff0c;开发了三维电子沙盘、AI三维电子沙盘、WEB三维地球、移动端三维地球、数字武装三维电子沙盘、智慧动员三维电子沙盘、智慧公安三维电子沙盘、智慧安监三维电子沙盘、森林防…

vue重修003

文章目录 版权声明day03一、今日目标1.生命周期2.综合案例-小黑记账清单3.工程化开发入门4.综合案例-小兔仙首页 二、Vue生命周期三、Vue生命周期钩子四、生命周期钩子小案例1.在created中发送数据2.在mounted中获取焦点 五、案例-小黑记账清单1.需求图示&#xff1a;2.需求分析…

Maven项目在pom.xml里配置远程仓库

如图:作用 在项目的 pom.xml 文件中配置了 <repositories> 元素&#xff0c;Maven会优先使用项目级别的仓库配置&#xff0c;而不会查找全局设置文件中的仓库配置。换句话说&#xff0c;项目级别的配置会覆盖全局设置文件中的仓库配置。 这意味着当在项目的 pom.xml 文…

AUTOSAR 面试知识回顾

如果答不上来&#xff0c;就讲当时做了什么 1. Ethernet基础: 硬件接口&#xff1a; ECU到PHY&#xff1a; data 是MII总线&#xff0c; 寄存器控制是SMI总线【MDCMDIO两根线, half duplex】PHY输出(100BASE-T1)&#xff1a; MDI总线&#xff0c;2 wire 【T1: twisted 1 pair …

C++项目:仿muduo库实现高性能高并发服务器

文章目录 一、实现目标二、前置知识&#xff08;一&#xff09;HTTP服务器1.概念 &#xff08;二&#xff09;Reactor模型&#xff1a;1.概念2.分类&#xff08;1&#xff09;单Reactor单线程&#xff1a;单I/O多路复用业务处理。&#xff08;2&#xff09;单Reactor多线程&…

腾讯mini项目-【指标监控服务重构-会议记录】2023-07-26

2023-07-26组长会议纪要 A组 项目对齐和问题 分配需求&#xff0c;SLI指标上报&#xff0c;暂时没有实际效果 每个人负责一条指标&#xff0c;同步代码&#xff0c;时间问题还是难题跟B组同学请教&#xff0c;答疑 问题&#xff1a;启动 Tracer 【已解决】 环境问题&#xf…

21.redo日志(下)

title: “redo日志&#xff08;下&#xff09;” createTime: 2022-03-06T15:52:4108:00 updateTime: 2022-03-06T15:52:4108:00 draft: false author: “ggball” tags: [“mysql”] categories: [“db”] description: “” redo log的刷盘时机 log buffer 空间不足时&…

【linux】性能优化

这张图谱出自倪朋飞&#xff1b; 1、什么是性能指标 这里一定会想到 “高并发” 和 “响应快”&#xff0c;这里词正对应的就是 “吞吐” 和 “延时”。我们知道随着应用负载的体系&#xff0c;系统资源的使用就会提高&#xff0c;甚至达到极限。而性能问题的本质&#xff0c…

【CNN-FPGA开源项目解析】卷积层01--floatMult16模块

文章目录 (基础)半精度浮点数的表示和乘运算16位半精度浮点数浮点数的乘运算 floatMult16完整代码floatMult16代码逐步解析符号位sign判断指数exponent计算尾数fraction计算尾数fraction的标准化和舍位整合为最后的16位浮点数结果[sign,exponent,fraction] 其他变量宽度表alway…