顺序表的实现和练习

杂谈:

有些数据结构(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);// 初始容量设置为4if (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位置插入xpsl->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.rhkb.cn/news/140694.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于地址存放的例题

unsigned int a 0x1234; unsigned char b *(unsigned char*)&a; 上面代码大端存储和小端存储的值分别是多少&#xff1f; 大端存储的是把高位地址存放在低位地址处&#xff0c;低位存放到高位。小端是高位存放在高位&#xff0c;低位在低位。因为a是整型&#xff0c;所…

Kafka的消息存储机制

前面咱们简单讲了K啊开发入门相关的概念、架构、特点以及安装启动。 今天咱们来说一下它的消息存储机制。 前言&#xff1a; Kafka通过将消息持久化到磁盘上的日志文件来实现高吞吐量的消息传递。 这种存储机制使得Kafka能够处理大量的消息&#xff0c;并保证消息的可靠性。 1…

vue重修003

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

ChatGPT批量写作文章软件

什么是ChatGPT批量写作文章。简单来说&#xff0c;它是一种使用ChatGPT技术的方法&#xff0c;可以帮助您批量生成各种类型的文章和内容。无论您是需要新闻报道、博客文章、产品描述、社交媒体帖子还是其他类型的内容&#xff0c;ChatGPT都能满足您的需求。它可以在极短的时间内…

探索 GO 项目依赖包管理与Go Module常规操作

探索 GO 项目依赖包管理与Go Module常规操作 文章目录 探索 GO 项目依赖包管理与Go Module常规操作一.Go 构建模式的演变1.1 GOPATH &#xff08;初版&#xff09;1.1.1 go get 1.2 vendor 机制&#xff08;中版&#xff09;1.3 Go Module&#xff08;最新版&#xff09; 二.创…

C语言 cortex-A7核UART总线实验

一、C 1&#xff09;uart4.h #ifndef __UART4_H__ #define __UART4_H__ #include "stm32mp1xx_rcc.h" #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_uart.h&quo…

4k、VR与万兆光网

“全光万兆”对VR意义重大。 pico4的分辨率 PICO 4 的单眼分辨率是 2160 2160&#xff0c;整体分辨率高达 4320 2160。这是一款高性能的 VR 一体机&#xff0c;采用了 2.56 英寸的 Fast-LCD 屏幕&#xff0c;最高可实现 90Hz 刷新率&#xff0c;还有 1200 PPI 和 20.6 PPD 的…

基于海康Ehome/ISUP接入到LiveNVR实现海康摄像头、录像机视频统一汇聚,做到物联网无插件直播回放和控制

LiveNVR支持海康NVR摄像头通EHOME接入ISUP接入LiveNVR分发视频流或是转GB28181 1、海康 ISUP 接入配置2、海康设备接入2.1、海康EHOME接入配置示例2.2、海康ISUP接入配置示例 3、通道配置3.1、直播流接入类型 海康ISUP3.2、海康 ISUP 设备ID3.3、启用保存3.4、接入成功 4、相关…

Mac磁盘空间满了怎么办?Mac如何清理磁盘空间

你是不是发现你的Mac电脑存储越来越满&#xff0c;甚至操作系统本身就占了100多G的空间&#xff1f;这不仅影响了电脑的性能&#xff0c;而且也让你无法存储更多的重要文件和软件。别担心&#xff0c;今天这篇文章将告诉你如何清除多余的文件&#xff0c;让你的Mac重获新生。 一…

Nginx的location作用

location Nginx 的 locaiton 作⽤是根据⽤户请求的 URI 不同&#xff0c;来执行不同的应用。针对用户请求的网站URL 进行匹配&#xff0c;匹配成功后进行对应的操作。 location [ | ~| ~* | ^~ ] url {#指定对应的动作 } 正则表达式解释 匹配符 匹配规则 优先级 精确匹配 1…

数据结构——二叉树层序遍历

链式二叉树的建立 前言一、层序遍历的概念和实现二、判断二叉树是否是完全二叉树总结 前言 来喽来喽~ 二叉树的层序遍历来喽~ 层序遍历那是相当有趣滴&#xff01; 我的朋友&#xff0c;请不要迷惘&#xff0c;你要记住&#xff0c;你终有鲲鹏一日&#xff01; 加油吧&#xf…

详解MySQL存储引擎

前言: 📕作者简介:热爱编程的小七,致力于C、Java、Python等多编程语言,热爱编程和长板的运动少年! 📘相关专栏Java基础语法,JavaEE初阶,数据库,数据结构和算法系列等,大家有兴趣的可以看一看。 😇😇😇有兴趣的话关注博主一起学习,一起进步吧! 一、MySQL存…

修改vscode底部栏背景和字体颜色

修改vscode底部栏背景和字体颜色 如图&#xff1a; 首先打开齿轮&#xff0c;打开设置搜索workbench.colorCustomizations,然后点击编辑setting.json修改setting.json内内容 "workbench.colorCustomizations": {"statusBar.foreground": "#FFFFFF…

5G通信与蜂窝模组之间的关系

5G通信是第五代移动通信技术的简称&#xff0c;它代表了一种新一代的无线通信技术标准。5G通信的主要目标是提供更高的数据传输速度、更低的延迟、更大的网络容量以及更可靠的连接&#xff0c;以支持各种新兴应用和服务&#xff0c;包括高清视频流、虚拟现实、物联网&#xff0…

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

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

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

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

linux 安装 wordpress

文章目录 linux 安装 wordpress1. wordpress 简介2. wordpress功能和特点3. 部署要求4. 环境搭建4.1 部署 nginx4.1.1 新增配置文件 4.2 部署 PHP74.2.1 查看当前版本4.2.2 YUM 安装 PHP74.2.3 查看 PHP 版本4.2.4 启动PHP-FPM4.2.5 修改配置文件4.2.6 重启服务 4.3 部署 mysql…

IDEA下使用Spring MVC

<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://ma…

【Git】轻松学会 Git:深入理解 Git 的基本操作

文章目录 前言一、创建 Git 本地仓库1.1 什么是仓库1.2 创建本地仓库1.3 .git 目录结构 二、配置 Git三、认识 Git 的工作区、暂存区和版本库3.1 什么是 Git 的工作区、暂存区和版本库3.2 工作区、暂存区和版本库之间的关系 四、添加文件4.1 添加文件到暂存区和版本库中的命令4…

设计模式之备忘录模式

文章目录 游戏角色状态恢复问题传统方案解决游戏角色恢复传统的方式的问题分析备忘录模式基本介绍游戏角色恢复状态实例备忘录模式的注意事项和细节 游戏角色状态恢复问题 游戏角色有攻击力和防御力&#xff0c;在大战 Boss 前保存自身的状态(攻击力和防御力)&#xff0c;当大…