数据结构:各种结构函数参数辨析

(一)顺序表

1)结构

typedef int SLDateType;typedef struct SeqList
{SLDateType* data;int size;int capacity;
}SeqList;SeqList ps = { 0 };

2)函数参数

// 对数据的管理:增删查改 
void SeqListInit(SeqList* ps);
void SeqListDestroy(SeqList* ps);void SeqListPrint(SeqList* ps);
void SeqListPushBack(SeqList* ps, SLDateType x);
void SeqListPushFront(SeqList* ps, SLDateType x);
void SeqListPopFront(SeqList* ps);
void SeqListPopBack(SeqList* ps);// 顺序表查找
int SeqListFind(SeqList* ps, SLDateType x);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDateType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos); 

3)分析

这里我们要实现函数的参数怎么设置,其实是根据我们实际需求来设计的。

我们的需求是:

创建一个结构体SeqList ps ,然后通过结构体 ps 访问 data, 在通过 data指针可以找到我们 malloc 空间的起始地址,我们可以通过这个起始地址控制 malloc 的整块空间,以使我们可以在这块空间上进行增删查改等操作。

当我们创建结构体SeqList ps 时,实际上是在内存栈区开辟了一块空间,这块空间存储着三个变量:*data、size、capacity;又因为data是一个指针变量,它又指向一块malloc在堆区开辟的空间,我们所有的数据都将在这块空间上处理。

如果函数传参,传的不是结构体SeqList ps 的地址的话,而是直接将结构体传给函数,这样就会出错,因为这就是典型的传值调用,此时,函数用的参数只是原变量的临时拷贝。

这样我们改变 ps1 ,但 ps 的内容没有改变。要使函数使用的空间和ps相同,就将ps的地址传过去就行了。

所以,函数的参数要传结构体的地址

(二)单向链表:

1)结构

SListNode* phead = NULL;

typedef int SLTDateType;typedef struct SListNode
{SLTDateType data;struct SListNode* next;
}SListNode;

2)函数参数

由于实现链表可以用两种结构:带哨兵位头节点和不带哨兵位头节点的两种结构,所以函数的参数类型也会有差异,具体类型详见下列分析。

3)分析

  • 将结构体 SListNode1 的地址作为参数(带哨兵位的头节点、一级指针接收)

 与上述顺序表相同的道理,我们可以将结构体 SListNode1 的地址作为参数,但是这样就要求SListNode1 必须要在调用函数前创建,其实这样就是将第一个节点作为哨兵位节点,这个节点不存储数据,只是用来作为链表开头的标记。

在链表中,哨兵节点(也称为虚拟节点或标志节点)是一个特殊的节点,通常不存储实际的数据,而是用于简化链表的操作。哨兵节点位于链表的起始或末尾,它不存储有效的数据,但可以帮助在某些情况下简化代码逻辑。

因此我们的代码如下:

void SListPushBack(SListNode* plist, SLTDateType x)
{// 创建新的节点SListNode* NewNode = (SListNode*)malloc(sizeof(SListNode));NewNode->data = x;NewNode->next = NULL;// 找到尾节点SListNode* current = plist;while (current->next != NULL){current = current->next;}current->next = NewNode;
}int main()
{SListNode node1;SListPushBack(&node1, 1);SListPushBack(&node1, 2);SListPushBack(&node1, 3);return 0;
}

 

 图解:

小结:

使用带哨兵位的头节点时,函数接收节点的类型为:SListNode*

  •  用一个指针指向头节点(二级指针接收)

 定义一个指针phead ,该指针指向NULL

 由图中可以看出:head指向NULL,所以显示其内部的变量data、next显示“无法读取内存”;

因为我们想要phead指向头节点,而节点又是 SListNode 类型的,所以phead就需要是 SListNode*类型;

在第一次创建节点时,将phead的指向NULL变为指向头节点,这样就改变了phead:

既然改变了phead,就需要传递phead的地址,想要函数接收phead的地址,就要phead变量类型的指针,phead的变量类型是 SListNode*,所以这个指针就需要是SListNode** 类型的。

 

此时phead就不再指向NULL了,而是指向第一个节点(其内部存储的是第一个节点的地址)

通过pplist的解引用就可以找到phead,再通过phead的解引用就可以找到节点的数据域和指针域,进而我们就可以改变节点的数据域,使节点能够存储数据;也可以改变节点的指针域,使各个节点能够连接起来

 小结:

不使用使用带哨兵位的头节点,使用指针指向头节点时,函数接收节点的类型为:SListNode**

当然,并不是所有 “用一个指针指向头节点” 的情形都需要使用二级指针来接收,还可以用函数的返回进行传递。

  • 利用函数返回值(一级指针接收)

这个结构和上面 “用一个指针指向头节点” 结构和原理是一样的,两者的主要差异就在于这次我们利用了初始化函数的返回值。

先来看一下初始化函数是怎样实现的:

ListNode* ListInit(ListNode* plist)
{plist = (ListNode*)malloc(sizeof(ListNode));plist->next = NULL;return plist;
}ListNode* head = NULL;
head = ListInit(head);

 图解:

第一步:

 

第二步:

 

 此时 head 就已经指向链表的头节点了,通过head的解引用就可以找到节点的数据域和指针域,进而我们就可以改变节点的数据域,使节点能够存储数据;也可以改变节点的指针域,使各个节点能够连接起来。

我们会发现这种方法根上面 “用一个指针指向头节点” 是一模一样的,其实上面一种方法在改变phead的指向后,其他函数的参数就可以可以是phead了,但是必须要在调用插入函数后,才能这样做。当我们先调用其他函数时,就会出错了,所以为了保险起见,我们统一将二级指针作为函数 的参数。

(三)栈

实现栈可以用数组也可以使用链表,而数组的结构对应的是顺序表的结构,所以对于栈的结构这里我就不在赘述了。

	Stack stack;
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{STDataType* a;int top; // 栈顶int capacity; // 容量
}Stack;// 初始化栈
void StackInit(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType data);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool StackEmpty(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);

(四)队列

这里利用链表实现队列是比较好的,所以队列的结构对应的是链表的结构。需要注意的是,为了便于尾插和获得队列的末尾元素,我们不仅定义了指向头节点的指针,还定义了指向尾节点的指针,对于这两个指针我们又定义了一个结构体。

Queue q;

typedef int QDataType;//链式结构:表示队列
typedef struct QListNode
{struct QListNode* next;QDataType data;
}QNode;// 队列的结构
typedef struct Queue
{QNode* head;QNode* tail;
}Queue;

图解:

 


本次内容到此结束了!如果你觉得这篇博客对你有帮助的话 ,希望你能够给我点个赞,鼓励一下我。感谢感谢……

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

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

相关文章

Linux系统目录结构介绍

Linux系统目录结构介绍 一、目录结构 Linux系统的目录结构是一颗倒状树: “/”表示最顶层的目录,叫做根目录。 (1)pwd可以显示当前所在的目录。 (2)cd可以切换当前的目录,例如,…

【Linux】TCP协议——传输层

目录 TCP协议 谈谈可靠性 TCP协议格式 序号与确认序号 窗口大小 六个标志位 确认应答机制(ACK) 超时重传机制 连接管理机制 三次握手 四次挥手 流量控制 滑动窗口 拥塞控制 延迟应答 捎带应答 面向字节流 粘包问题 TCP异常情况 TC…

【文献阅读笔记】深度异常检测模型

文章目录 导读相关关键词及其英文描述记录深度异常检测模型Supervised deep anomaly detection 有监督深度异常检测Semi-Supervised deep anomaly detection 半监督深度异常检测Hybrid deep anomaly detection 混合深度异常检测One-class neural network for anomaly detection…

心跳跟随的心形灯(STM32(HAL)+WS2812+MAX30102)

文章目录 前言介绍系统框架原项目地址本项目开发开源地址硬件PCB软件功能 详细内容硬件外壳制作WS2812级联及控制MAX30102血氧传感器0.96OLEDFreeRTOS 效果视频总结 前言 在好几年前,我好像就看到了焊武帝 jiripraus在纪念结婚五周年时,制作的一个心跳跟…

Android进阶之SeekBar动态显示进度

SeekBar 在开发中并不陌生,默认的SeekBar是不显示进度的,当然用吐司或者文案在旁边实时显示也是可以的,那能不能移动的时候才显示,默认不显示呢,当然网上花哨的三方工具类太多了,但是我只是单纯的想在SeekBar的基础上去添加一个可以跟随移动显示的气泡而…

Python爬虫在电商数据挖掘中的应用

作为一名长期扎根在爬虫行业的专业的技术员,我今天要和大家分享一些有关Python爬虫在电商数据挖掘中的应用与案例分析。在如今数字化的时代,电商数据蕴含着丰富的信息,通过使用爬虫技术,我们可以轻松获取电商网站上的产品信息、用…

在WebStorm中通过live-server插件搭建Ajax运行环境

1.下载node.js 官网: https://nodejs.cn/download/ 2.配置Node.js的HTTPS 使用淘宝的镜像: npm config set registry https://registry.npm.taobao.org 也可以使用cnpm npm install -g cnpm --registryhttps://registry.npm.taobao.org 配置之后可以验证是否成…

http相关知识点

文章目录 长链接http周边会话保持方案1方案2 基本工具postmanFiddlerFiddler的原理 长链接 一张网页实际上可能会有多种元素组成,这也就说明了网页需要多次的http请求。可由于http是基于TCP的,而TCP创建链接是有代价的,因此频繁的创建链接会…

中国信息安全测评中心CISP家族认证一览

随着国家对网络安全的重视,中国信息安全测评中心根据国家政策、未来趋势、重点内容陆续增添了很多CISP细分认证。 今日份详细介绍,部分CISP及其子品牌相关认证内容,一定要收藏哟! 校园版CISP NISP国家信息安全水平考试&#xff…

【C# 基础精讲】循环语句:for、while、do-while

循环语句是C#编程中用于重复执行一段代码块的关键结构。C#支持for、while和do-while三种常见的循环语句,它们允许根据条件来控制代码块的重复执行。在本文中,我们将详细介绍这三种循环语句的语法和使用方法。 for循环 for循环是一种常见的循环结构&…

【Linux】云服务器自动化部署VuePress博客(Jenkins)

前言 博主此前是将博客部署在 Github Pages(基于 Github Action)和 Vercel 上的,但是这两种部署方式对于国内用户很不友好,访问速度堪忧。因此将博客迁移到自己的云服务器上,并且基于 Jenkins(一款开源持续…

代码审计-Thinkphp框架审计前置知识点

代码审计必备知识点: 1、代码审计开始前准备: 环境搭建使用,工具插件安装使用,掌握各种漏洞原理及利用,代码开发类知识点。 2、代码审计前信息收集: 审计目标的程序名,版本,当前环境(系统,中间件…