用C语言实现链式存储结构 万字

news2024/11/13 11:02:21

各位同学,大家好,我叫小敖。今天给大家分享数据结构之一链式存储结构,下面是对链表简单介绍,希望大家能理解。

链表介绍

链表是一种物理存储单元上非连续、非顺序的存储结构**,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1).

在这里插入图片描述

顺序表和链表差异

顺序表是一种连续的存储结构,数据元素在内存中占据一块连续的存储空间。它可以通过下标来直接访问元素,因此支持随机访问。顺序表可以使用数组来实现,每个元素占据固定大小的内存空间。

顺序表优点

随机访问性能好:由于顺序表的元素在内存中连续存储,可以通过下标直接访问元素,因此随机访问的时间复杂度为O(1)。
存储效率高:顺序表不需要额外的存储空间来存储指针,只需要存储元素本身,因此相对于链表来说,存储效率较高

缺点:

插入和删除操作复杂:由于顺序表是连续存储的,当需要插入或删除元素时,需要移动其他元素来腾出空间或填补空缺,因此时间复杂度为(O n)
其中n是元素的个数。
存储空间固定:顺序表在初始化时需要指定固定大小的存储空间,如果元素数量超过了预设的大小,需要进行扩容操作,这可能导致时间和空间的浪费。看下图 模拟头删的操作 还是很不方便的。
在这里插入图片描述
链表(Linked List): 链表是一种离散的存储结构,数据元素存储在称为节点的单元中,每个节点 包含数据和指向下一个节点的指针通过链接各个节点,形成数据的逻辑顺序

优点:

插入和删除操作简单:由于链表的节点通过指针链接,插入和删除操作只需要修改指针的指向,不需要移动其他元素,因此时间复杂度为O(1)。
灵活使用内存空间:链表可以根据实际情况动态分配内存空间,只需要在插入新节点时分配新的内存,因此避免了固定大小的存储空间的限制。

缺点:

随机访问性能差:由于链表中的元素并不连续存储,访问元素需要通过指针依次遍历,因此随机访问的时间复杂度为O(n),其中n是元素的个数。
需要额外的存储空间:链表的每个节点需要额外的指针来指向下一个节点,这样会占用额外的存储空间,相对于顺序表来说,存储效率较低。
在这里插入图片描述
在这里插入图片描述
链表分为数值域指针域,每个指针域里存储下个元素的地址,最后一个指针域 存储的是空(NULL)。
在这里插入图片描述

链表内容和输出链表

首先我们创造三个文件 分别为SList.h的头文件 负责各个函数的声明,SList.c链表这个文件来定义函数功能 ,test来负责函数传参 给链表赋值。

链表的定义

SList.h

typedef int SLTDataType; //类型重新定义
typedef struct SListNode//类型重新定义
{
	SLTDataType data;//数组域
	struct SListNode* next;//指针域
}SLTNode;

链表的打印
void SLTPrint(SLTNode* phead)
{
	SLTNode* cur = phead;//cur存储头节点
	while (cur != NULL)//如果不为空 就接着往下找 我们前面介绍过 最后一个节点next为空
	{
		printf("%d->", cur->data);//访问节点里data元素 并且打印
		cur = cur->next;//把下一个节点next赋给cur 然后进行循环
	}

	printf("NULL\n");//空地址 循环结束输出
}

我们知道每个节点里的指针域都存储下一个地址,所以靠next找下一个地址,然后结束以后打印个NULL,这里cur存储头节点是个好习惯,如果这个遍历链表函数打印完 ,还想在头部做动作那边 ,直接找phead就好。

链表的头插

我们在上面已经在SList.h文件声明了链表的 数据域和指针域,但是他只是个模型,实际上我们并没有创造链表,这时候就需要malloc一个结点。
SList.c

    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
  	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

这里通过malloc创建了个 节点,但是如果我们想进行头部插入话,还得两个指针域部分,然后把指针域的next 指向头。我们来详细拆分 先看完整代码。

void SLPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead); 
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->next = NULL;
	newnode->next = *pphead;
	*pphead = newnode;
}

在这里插入图片描述

这里创建一个链表SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));,我们知道头插 插的是链表newnode->data = x;//这里给链表的data存储赋值x
newnode->next = NULL;
//然后链表的next赋值为NULL
newnode->next = pphead;//新创建链表的next被原先头部地址赋值
pphead = newnode;
**//pphead原先头的位置 赋值给新的头 就是你动态开辟的newnode

链表头插总结

链表头部插入得malloc一个节点,然后给创建出来的节点data部位赋值,next部位指向原先头节点的位置(哪里NULL是个好习惯置空),然后pphead指向新的节点 另外提一嘴为什么要用二级指针,因为*pphead赋值的next得存储下一个节点位置。
> test.c
SLPushFront(&plist, 4);

链表尾插

首先我们知道链表尾部插入或者是头部插入,再或者是任意插入,都离不开要创造链表,那我们可不可以把这个创建链表的功能单独写成一个函数呢。

SList.c

SLTNode* BuyLTNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//这里创造个节点
	if (newnode == NULL)//然后判断
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;//data赋值x
	newnode->next = NULL;//next设置为空

	return newnode;//返回节点
}

接下来我们正式开始玩尾插的玩法了,我们先画图理解思路。这里定义了一个tail指针 ,我们找到尾端空就停下,把新开辟这个节点 赋值给tail最后那个位置 next处 然后尾部插入就成功了

`SLTNode* tail = *pphead;
while (tail->next != NULL)
{
	tail = tail->next;
}
tail->next = newnode;`

在这里插入图片描述

但是我们得考虑一种假设链表为空呢?那我们这里就判断一下链表是不是为空,是的话直接插入就可以了。

if (*pphead == NULL)
{
	*pphead = newnode;
}
else
{
	SLTNode* tail = *pphead;
	while (tail->next != NULL)
	{
		tail = tail->next;
	}
	tail->next = newnode;
}
链表头删

我们先理解思路,我们用del来存储头指针位置pphead,然后我们pphead往下走一步 然后释放掉del 这样链表就头删成功了。好这里不过多分享

在这里插入图片描述

void SLPopFront(SLTNode** pphead)
{
	assert(pphead); // 链表为空,pphead也不为空,因为他是头指针plist的地址
	assert(*pphead); // 链表为空,不能头删。(当然你还可以用温柔的检查)

	SLTNode* del = *pphead;
	*pphead = (*pphead)->next;
	free(del);

链表尾删

首先如果链表为空就不能删除了,如果我们直接暴力检查的一下。

assert(pphead); 
assert(*pphead); 

如果只有一个节点话 ,我们是不是就自己删除了,直接free掉就好。

if ((*pphead)->next == NULL)
{
	free(*pphead);
	*pphead = NULL;
}



```c
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		
		SLTNode* tail = *pphead;//头节点
		// 找尾
		while (tail->next->next)//next next为空停止循环
		{
			tail = tail->next;
		}

		free(tail->next);
		tail->next = NULL;
	}
}

在这里插入图片描述

链表查找

如何链表查找 很简单 我们遍历链表 判断数值域部分
是不是等于我们要查找数字就好,如果是就返回,如果不是得接着指向下一个位置,如果查不到就返回空指针
代码如下 这里不做过多解释

SLTNode* STFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;
	}

	return NULL;
}
在pos之前插入

我们要在pos之前新插入一个链表,那是不是我们得找到pos这个位置呀
但是我们如果,定义一个头部指针让他每次都走向下一个,那么假设pos就是第一个呢?很简单我们头插就好。

if (*pphead == pos)
{
	SLPushFront(pphead, x);
}
while (prev->next != pos)
{
	prev = prev->next;
}

在这里插入图片描述

void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);
	//assert(*pphead);

	if (*pphead == pos)
	{
		SLPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		SLTNode* newnode = BuyLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

如果prev第一个节点等于pos就进行头插,否则就是pphead赋值给prev,prev->next等于pos就停下来prev是pos前一个节点,然后创建一个节点,prev 指针域等于next=newnode这个节点 然后newnode这个指针域等于pos位置地址.

在pos之后插入
// 在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);

	SLTNode* newnode = BuyLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

这里思路是 创建一个节点,然后在创造节点next位置 指针域里存储pos->next下一个节点,pos->next节点等于newnode地址。

删除pos任意位置

这里的思路了和pos之前删除差不多不做过多解释,直接附上代码。

void SLErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	if (pos == *pphead)
	{
		SLPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		prev->next = pos->next;
		free(pos);
	}
}
删除中间节点
void SLEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);

	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
}

在这里插入图片描述
读一次 del赋值pos next节点就是第二个节点 然后让pos->next=del->next 等于第三个节点,然后释放掉中间节点。
剩下给大家展示完整的代码

SList.h
#pragma once

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

typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

void SLTPrint(SLTNode* phead);
void SLPushFront(SLTNode** pphead, SLTDataType x);
void SLPushBack(SLTNode** pphead, SLTDataType x);

void SLPopFront(SLTNode** pphead);
void SLPopBack(SLTNode** pphead);

// 单链表查找
SLTNode* STFind(SLTNode* phead, SLTDataType x);

// 在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
void SLInsertAfter(SLTNode* pos, SLTDataType x);

// 删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos);

// 删除pos位置后面的值
void SLEraseAfter(SLTNode* pos);




SList.c
#include"Slist.h"
void SLTPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}

	printf("NULL\n");
}

SLTNode* BuyLTNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

void SLPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);  // 链表为空,pphead也不为空,因为他是头指针plist的地址
	//assert(*pphead); // 不能断言,链表为空,也需要能插入

	SLTNode* newnode = BuyLTNode(x);

	newnode->next = *pphead;
	*pphead = newnode;
}

//void SLPushBack(SLTNode* phead, SLTDataType x)
//{
//	SLTNode* tail = phead;
//	while (tail != NULL)
//	{
//		tail = tail->next;
//	}
//
//	SLTNode* newnode = BuyLTNode(x);
//	tail = newnode;
//}

void SLPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead); // 链表为空,pphead也不为空,因为他是头指针plist的地址
	//assert(*pphead); // 链表为空,可以尾插

	SLTNode* newnode = BuyLTNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		tail->next = newnode;
	}
}

void SLPopFront(SLTNode** pphead)
{
	assert(pphead); // 链表为空,pphead也不为空,因为他是头指针plist的地址
	assert(*pphead); // 链表为空,不能头删。(当然你还可以用温柔的检查)

	SLTNode* del = *pphead;
	*pphead = (*pphead)->next;
	free(del);

	// 一个节点
	// 多个节点
	//if ((*pphead)->next == NULL)
	//{
	//	free(*pphead);
	//	*pphead = NULL;
	//}
	//else
	//{
	//	SLTNode* del = *pphead;
	//	//*pphead = del->next;
	//	*pphead = (*pphead)->next;
	//	free(del);
	//}
}

void SLPopBack(SLTNode** pphead)
{
	assert(pphead); 
	assert(*pphead); 

	// 没有节点(空链表)

	// 暴力检查
	//assert(*pphead);

	// 温柔的检查
	/*if (*pphead == NULL)
	{
		return;
	}*/

	// 一个节点
	// 多个节点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		
		SLTNode* tail = *pphead;
		// 找尾
		while (tail->next->next)
		{
			tail = tail->next;
		}

		free(tail->next);
		tail->next = NULL;
	}
}

SLTNode* STFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;
	}

	return NULL;
}

// 在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);
	//assert(*pphead);

	if (*pphead == pos)
	{
		SLPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		SLTNode* newnode = BuyLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

// 在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);

	SLTNode* newnode = BuyLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

// 删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	if (pos == *pphead)
	{
		SLPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		prev->next = pos->next;
		free(pos);
	}
}

void SLEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);

	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
}
test.c
#include"SList.h"

void TestSList1()
{
	SLTNode* plist = NULL;
	SLPushFront(&plist, 1);
	SLPushFront(&plist, 2);
	SLPushFront(&plist, 3);
	SLPushFront(&plist, 4);

	/*plist = SLPushFront(plist, 1);
	plist = SLPushFront(plist, 2);
	plist = SLPushFront(plist, 3);
	plist = SLPushFront(plist, 4);*/

	SLTPrint(plist);

	SLPushBack(&plist, 5);
	SLTPrint(plist);
}

void TestSList2()
{
	SLTNode* plist = NULL;
	SLPushBack(&plist, 1);
	SLPushBack(&plist, 2);
	SLPushBack(&plist, 3);
	SLPushBack(&plist, 4);
	SLTPrint(plist);

	SLPopBack(&plist);
	SLTPrint(plist);
	SLPopBack(&plist);
	SLTPrint(plist);
	SLPopBack(&plist);
	SLTPrint(plist);
	SLPopBack(&plist);
	SLTPrint(plist);

	//SLPopBack(&plist);
}

void TestSList3()
{
	SLTNode* plist = NULL;
	SLPushBack(&plist, 1);
	SLPushBack(&plist, 2);
	SLPushBack(&plist, 3);
	SLPushBack(&plist, 4);
	SLTPrint(plist);

	SLTNode* pos = STFind(plist, 3);
	if (pos)
		pos->data = 30;
	SLTPrint(plist);
}


void TestSList4()
{
	SLTNode* plist = NULL;
	SLPushBack(&plist, 1);
	SLPushBack(&plist, 2);
	SLPushBack(&plist, 3);
	SLPushBack(&plist, 4);
	SLTPrint(plist);

	SLTNode* pos = STFind(plist, 3);
	if (pos)
	{
		SLInsert(&plist, pos, 30);
	}
	SLTPrint(plist);

	pos = STFind(plist, 2);
	if (pos)
	{
		SLInsertAfter(pos, 20);
	}
	SLTPrint(plist);

	pos = STFind(plist, 2);
	if (pos)
	{
		SLErase(&plist, pos);
	}
	SLTPrint(plist);
}

int main()
{
	TestSList4();

	return 0;
}

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

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

相关文章

大模型快速部署,以浪潮源2.0为例

step1: 申请PAI-DSW试用 step2&#xff1a;魔塔社区授权 由于本地授权一直失败&#xff0c;于是采用了魔塔免费平台实例进行学习。 搭建好之后&#xff0c;打开就有相关页面了&#xff1a; demo搭建&#xff1a; 按照官方提示的步骤进行搭建&#xff0c;内容如下&#xff1a;…

第二十一节、敌人追击状态的转换

一、物理检测中的Boxcast 1、检测敌人Bool 当不知道一个函数的返回值是什么的时候 定义一个var变量 就知道了 二、状态切换 1、switch用法 2、新的语法糖写法

ubuntu2004上的glib编译教程

最近因为工作需要编译glib&#xff0c;写文章记录一下编译流程。 从launchpad上下载源码&#xff1a;链接 根据control文件的提示安装对应的依赖 然后尝试debuild&#xff0c;这里会编译不过出去&#xff0c;不过debuild会自动生成一些编译文件&#xff0c;不要删除。 接下来…

2024云南导游资格证题库

1、释迦三圣是( )。 A、药师佛 B、文殊菩萨 C、释迦牟尼 D、普贤菩萨 E、观音菩萨 答案&#xff1a;BCD 2、下列女士套裙穿法中&#xff0c;错误的是&#xff08; &#xff09;。 A、上衣的袖长不超过着装者的手腕&#xff0c;裙子不盖过脚踝 B、女士在正式场合穿…

八、MyBatis

一、MyBatis介绍 MyBatis 是持久层框架&#xff0c;它支持自定义 SQL、存储过程以及⾼级映射。MyBatis 去除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置 和映射原始类型、接口和 Java POJO&#xff08;Plain Old Java Obj…

数据结构--第七天

递归 -递归的概念 递归其实就是一种解决问题的办法&#xff0c;在C语言中&#xff1a;递归就是函数自己调用自己 -递归的思想 递归的思考方式就是把大事化小的过程 递归的递就是递推的意思&#xff0c;归就是回归的意思 &#xff08;递归是少量的代码完成大量的运算&#xff09…

数据结构(5.5_2)——并查集

逻辑结构——数据元素之间的逻辑关系 并查集&#xff1a; 并查集&#xff08;Union-Find&#xff09;是一种树型的数据结构&#xff0c;用于处理一些不交集的合并及查询问题。它支持两种操作&#xff1a; 用双亲表示存储并查集 首先将所有根节点数组值设为-1&#xff0c;其…

[Android] [解决]Bottom Navigation Views Activity工程带来的fragment底部遮盖的问题

创建了Bottom Navigation Views Activity之后&#xff0c;在fragment_home.xml&#xff0c;加了一个RecyclerView&#xff0c; 后来添加了item之后发现底部会被盖住一部分。 解决&#xff1a;在layout里面加两句&#xff1a; android:paddingBottom"?attr/actionBarSize&…

C#获取Network的相关信息

1&#xff0c;获取网络的通断。 //方法1&#xff1a;无效果&#xff0c;并不能反映当前网络通断 bool availableSystem.Windows.Forms.SystemInformation.Network//方法2&#xff1a;通过VB获取网络状态&#xff0c;可反映当前网络通断 Microsoft.VisualBasic.Devices.Network…

Qt QML 使用QPainterPath绘制弧形曲线和弧形文本

Qt并没有相关api直接绘制弧形文字&#xff0c;但提供了曲线绘制相关类&#xff0c;所以只能另辟蹊径&#xff0c;使用QPainterPath先生成曲线&#xff0c;然后通过曲线上的点来定位每个文字并draw出来。 QML具体做法为从QQuickPaintedItem继承&#xff0c;在派生类中实现paint…

Linux 系统框架分析(一)

一、linux内核结构框图 对内核结构框图有个总体的把握&#xff0c;有助于理解为什么驱动要这样写&#xff0c;为什么写的应用程序所用的C库接口能够产生这么多的事情。 框图可以看出来&#xff0c;linux系统&#xff0c;包括五个系统 一、Linux内核结构介绍 Linux 内核是操作…

【机器学习基础】线性回归

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科&#xff0c;通过算法和模型让计算机从数据中学习&#xff0c;进行模型训练和优化&#xff0c;做出预测、分类和决策支持。Python成为机器学习的首选语言&#xff0c;…

Flutter 生成图表(fl_chart)

Flutter 图表 使用fl_chart 先看最终效果 实现抖音’使用管理助手’效果 需求分析&#xff1a;统计每个用户近7天每天的使用时长&#xff08;从当天往后推导&#xff0c;假设今天周二&#xff09;&#xff0c;单位为分钟或者小时&#xff0c;根据平均时长决定 技术选型&…

Python基于TensorFlow实现卷积神经网络-双向长短时记忆循环神经网络回归模型(CNN-BiLSTM回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 随着深度学习技术的发展&#xff0c;尤其是卷积神经网络&#xff08;Convolutional Neural Networks, …

Android Studio 连接手机进行调试

总所周知&#xff0c;Android Studio里的虚拟手机下载后又大又难用。不如直接连手机用。本篇文章主要内容为Android Studio怎么连接手机进行程序调试。 1. 在AndroidSDK中下载google USB Driver: 2. 连接手机&#xff1a; 进入电脑设备管理器界面。并点开便携设备&#xff0c…

wakanda:1靶机渗透测试

一、靶机下载地址 https://download.vulnhub.com/wakanda/wakanda-1.ova 二、信息收集 1、主机发现 # 使用命令 nmap 192.168.222.0/24 -sn | grep -B 2 "08:00:27:ED:98:E1" 2、端口扫描 # 使用命令 nmap 192.168.222.149 -p- -sV 3、指纹识别 # 使用命令 wha…

C++相关概念和易错语法(25)(列表初始化、initializer_list)

列表初始化是C11中新增的初始化规则&#xff0c;大大增强了初始化的灵活性&#xff0c;在本篇文章中&#xff0c;我会详细讲解列表初始化的各种使用场景。 1.从C语言初始化语法开始改变——不带赋值号的初始化方式 列表初始化可以说是将C语言的一些初始化语法进行放大的结果。…

c++习题18-计算鞍点

目录 一&#xff0c;题目 二&#xff0c;思路 三&#xff0c;代码 一&#xff0c;题目 给定一个55的矩阵&#xff0c;每行只有一个最大值&#xff0c;每列只有一个最小值&#xff0c;寻找这个矩阵的鞍点。鞍点指的是矩阵中的一个元素&#xff0c;它是所在行的最大值&#x…

海外短剧小程序 ,竖屏会员付费看剧系统搭建paypal,stripe对接支付功能

目录 前言&#xff1a; 一、系统功能 二、系统常见问题 总结&#xff1a; 前言&#xff1a; 在全球化的今天&#xff0c;短剧作为一种新兴的内容形式&#xff0c;正迅速赢得国际观众的心。尤其是海外市场的短剧推广&#xff0c;正成为内容创作者和营销者的新宠。本文将深入…

AWS云服务器选择最佳区域

2024年&#xff0c;随着全球云计算的持续发展和AWS在全球不断扩展的数据中心网络&#xff0c;选择合适的AWS云服务器区域成为了企业和开发者需要认真考虑的问题。九河云告诉你在做出选择之前&#xff0c;需要考虑以哪些关键因素&#xff1a; 地理位置和用户分布 选择AWS云服务…