当前位置: 首页 > news >正文

双向链表的初步练习

𝙉𝙞𝙘𝙚!!👏🏻‧✧̣̥̇‧✦👏🏻‧✧̣̥̇‧✦ 👏🏻‧✧̣̥̇:  Solitary-walk

      ⸝⋆   ━━━┓
     - 个性标签 - :来于“云”的“羽球人”。 Talk is cheap. Show me the code
┗━━━━━━━  ➴ ⷯ

本人座右铭 :   欲达高峰,必忍其痛;欲戴王冠,必承其重。

👑💎💎👑💎💎👑 
💎💎💎自💎💎💎
💎💎💎信💎💎💎
👑💎💎 💎💎👑    希望在看完我的此篇博客后可以对你有帮助哟

👑👑💎💎💎👑👑   此外,希望各位大佬们在看完后,可以互赞互关一下,看到必回
👑👑👑💎👑👑👑   

目录

 


1. 双向链表的初始化

2. 双向链表的销毁

3. 双向链表的尾插

4. 双向链表的头插

5. 双向链表的尾删

6.双向链表的头删

7. 双向链表的指定位置之后的插入

8. 双向链表的指定位置的删除
9. 双向链表的按值查找

10.链表打印


首先在我们进行各项具体任务之前,咱还是需要把前期工作准备好的 

1.先进行自定义链表的声明

2.一些函数的声明

3.必要头文件的引入

首先我们要知道什么是双向链表,顾名思义,对于一个结点而言既有指向自己的指针,也有指向其他结点的指针域

如图所示:

链表结构体类型的定义代码如下:

typedef struct ListNode 
{DataType data;//数据域struct ListNode* pre;//前驱域struct ListNode*next;//后继域
}ListNode;

 1.初始化

 注意这里的带头双向链表不同于前面单链表的头节点

为了好区分,这里我们称之为"哨兵位"

哨兵位只是占一个位置,并不存储任何有效的数据

 当我们只有哨兵位这个结点,思考一下,如何变成一个双向循环链表

你想对了吗??

	phead->pre = phead->next = phead;//让他变成双向循环链表

初始化代码之不采用 传参的方法

ListNode* Init()//初始化,采用不传参的形式,但是需把哨兵位这个结点返回
{//自己需要创建一个哨兵位ListNode* phead = (ListNode*)malloc(sizeof(ListNode));assert(phead);//可能开辟失败phead->data = -1;phead->pre = phead->next = phead;//记住这里要让他变成双向循环链表return phead;
}

初始化代码之采用 传参的方法

注意这里我们形参是用二级指针来接收的

因为实参我们需要传链表的地址,否则我们是无法完成初始化的

因为函数形参只是实参的一份临时拷贝,对形参的临时修改并不会影响我们的实参

void Init(ListNode** pphead)//初始化,采用传参的形式
{//注意一下操作都是基于 phead是哨兵位来进行的 他只占一个位置,并不存储任何有效数据assert(pphead);*pphead = (ListNode*)malloc(sizeof(ListNode));if (*pphead == NULL){perror("malloc fail\n");return 1;//既然开辟失败,没必要执行下面代码}(*pphead)->data = -1;//注意优先级顺序(*pphead)->next = (*pphead)->pre = NULL;//构建一个双向循环链表
}
2.销毁

说白了,其实就是一个结点一个结点进行删除

这自然就需要遍历了

我们在删除之前需要先找到删除结点后一个结点

关键是我们实参到底是指针还是指针地址

 让要删除的结点为 del = phead->next;

那么要删除的结点下一个结点为 del->next

我们不妨试一下~~~

实参为指针

void Destroy(ListNode* phead)//链表销毁;想一下,传一级指针还是二级?  在这里我们传入一级指针,为了保持接口一致性
{//销毁我们是一个一个进行删除,自然就需要遍历assert(phead);ListNode* del = phead->next;while (del != phead){ListNode* next = del->next;free(del);/*del = NULL;*/    //  ?del = next;}//来到这说明,此时只有一个哨兵位free(phead)phead = NULL;}

 注意当我们只是简单调用销毁函数的时候,代码并不是我们所设想的一样

正常来说,我们的plist的值应该为NULL

 

为了解决这个问题我们就需要在传一级指针的时候,手动把plist 置为空 

 实参为指针地址

 代码如下,但是问题又来了

void LTDestroy(ListNode** pphead) 
{assert(pphead && *pphead);ListNode* cur = (*pphead)->next;while ( cur!=*pphead ){ListNode* next = cur->next;free(cur);cur = next;}free(*pphead);*pphead = NULL;}

 对于这个问题容本up主先买个关子,欲知后事如何 且听下回分解

为了保持接口一致性,我们这里就使用一级指针来接收

3.尾插

顾名思义,是在尾结点后面进行插入,注意这里在哨兵位的前面进行插入也是可以滴~~~

接下来就是改变指针的指向

1)首先既然是尾插就需要为要插进来的数据开辟结点

这里涉及到创建结点的函数

2)其次是处理指针指向

3)先处理node的pre ,next

4)处理 原来尾结点,哨兵位

 尾插草图如下:

 结点创建函数的代码如下:

ListNode* ListBuyNode(x)//创建结点
{ListNode* node = (ListNode*)malloc(sizeof(ListNode));if (node == NULL){perror("malloc fail\n");return;}//来到这,说明空间开辟成功node->data = x;node->next = node->pre = NULL;return node;
}

 

 

 尾插完整代码:

void PushBack(ListNode* phead,DataType x)//尾插
{assert(phead);//为插入的数据开辟空间ListNode* node = ListBuyNode(x);//先处理node 的前驱,后继node->pre = phead->pre; //  phead->pre->next原来尾结点的后继,node->pre = phead->pre->next这里出现了野指针的问题 phead->pre->next没有具体指向node->next = phead;//接下来在处理 原来尾结点,哨兵位/*,这样写是错的,以下2句话不可以颠倒已经把node这个结点给覆盖掉了,在执行65行代码时,node 是未知的,因为这个结点已经被覆盖了,phead->pre->next此时他的指向也就是未知的,调试时不会报错,但是遍历读取他就会报错了,就像你在打印函数里进行打印时,出现野指针phead->pre = node;phead->pre->next = node;*/phead->pre->next = node;phead->pre = node;//别忘了,每尾插进来的结点最终都是一个新的尾结点
}

4.头插

首先是在哨兵位是后面进行的,每插入进来应该结点,此节点就会成为一个新的头节点

同上,需要先创建一个结点

其次找到原来头节点 phead->next 

 头插草图如下:

头插完整代码如下:

void PushFront(ListNode* phead,DataType x)//头插
{//头插是在哨兵位的后面进行插入assert(phead);//为插入的数据开辟空间ListNode* node = ListBuyNode(x);//处理node 的后继,前驱node->pre = phead;node->next = phead->next;//处理phead ,phead->next /*这样写也是对的phead->next->pre = node;*/phead->next = node;//头节点要进行更新phead->next->pre = node;}

5.尾删

注意我们不能上来就行删除

需要先找到要删除结点 (del)前一个也就是 del->pre

这里我们一定要注意结点的先后顺序

完整代码如下:

void PopBack(ListNode* phead)//尾删
{//注意不能直接删除,需要先找到尾结点的前一个结点assert(phead);//先判断以下是否为空,有2种写法/*if (phead->pre == phead){return 9;}*/assert(phead->next != phead);//是否为空ListNode* del = phead->pre;//把要删除的结点先保存起来//删除之前,先要构成一个新的双向循环链表del->pre->next = phead;phead->pre = del->pre;/*错误的,顺序不能颠倒,因为此时 del->pre已经被覆盖了phead->pre = del->pre;//新的尾结点del->pre->next = phead;*/free(del);del = NULL;}

 

7. 指定位置之后的插入
草图如下:

 

1)要为插入的数据创建结点

2)找到指定位置(pos)之后的结点 (pos->next)和之前的结点(pos->pre

3)  改变指针指向

指定位置之后插入对应完整代码:

void InsertAfter(ListNode* pos, DataType x)//指定位置之后插入
{assert(pos);//为插入的数据开辟空间ListNode* node = ListBuyNode(x);//处理 node 的前驱,后继node->next = pos->next;node->pre = pos;//处理 pos 和 pos->next的prepos->next = node;pos->next->pre = node;}
8. 双向链表的指定位置的删除

对应草图如下:

 直接进行删除是不行滴~~~

先要找到指定位置(pos)之后的结点 (pos->next)和之前的结点(pos->pre

其次改变指针走向

void Earse(ListNode* pos)//指定位置删除
{assert(pos);//需要找到pos之前的一个结点和之后的一个结点pos->next->pre = pos->pre;pos->pre->next = pos->next;free(pos);pos = NULL;
}
9. 双向链表的按值查找

 这里自然就需要一个一个进行遍历了

按值查找对应完整代码:

ListNode* Find(ListNode* phead, DataType x)//在链表中按值查找,若是找到则返回对应的结点
{assert(phead);ListNode* pcur = phead->next;//定义一个用来循环遍历的指针while (pcur != phead){if (pcur->data == x){return pcur;//直接返回对应结点}pcur = pcur->next;}printf("没有找到\n");
}

 10链表打印

方法同上,对链表进行遍历

void Print(ListNode* phead)//链表打印
{assert(phead);ListNode* pcur = phead->next;//定义一个用来 遍历的指针,注意他初始值是 phead->nextwhile (pcur != phead){printf("%d-> ", pcur->data);pcur = pcur->next;//记得更新}printf("\n");
}

 


List.c对应完整代码

#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"//void Init(ListNode** pphead)//初始化,采用传参的形式
//{
//	//注意一下操作都是基于 phead是哨兵位来进行的 他只占一个位置,并不存储任何有效数据
//	assert(pphead);
//	*pphead = (ListNode*)malloc(sizeof(ListNode));
//	if (*pphead == NULL)
//	{
//		perror("malloc fail\n");
//		return 1;//既然开辟失败,没必要执行下面代码
//	}
//
//	(*pphead)->data = -1;//注意优先级顺序
//	(*pphead)->next = (*pphead)->pre = NULL;//构建一个双向循环链表
//}ListNode* Init()//初始化,采用不传参的形式,但是需把哨兵位这个结点返回
{//自己需要创建一个哨兵位ListNode* phead = (ListNode*)malloc(sizeof(ListNode));assert(phead);//可能开辟失败phead->data = -1;phead->pre = phead->next = phead;//记住这里要让他变成双向循环链表return phead;
}
ListNode* ListBuyNode(x)//创建结点
{ListNode* node = (ListNode*)malloc(sizeof(ListNode));if (node == NULL){perror("malloc fail\n");return;}//来到这,说明空间开辟成功node->data = x;node->next = node->pre = NULL;return node;
}
void Print(ListNode* phead)//链表打印
{assert(phead);ListNode* pcur = phead->next;//定义一个用来 遍历的指针,注意他初始值是 phead->nextwhile (pcur != phead){printf("%d-> ", pcur->data);pcur = pcur->next;//记得更新}printf("\n");
}void PushBack(ListNode* phead,DataType x)//尾插
{assert(phead);//为插入的数据开辟空间ListNode* node = ListBuyNode(x);//先处理node 的前驱,后继node->pre = phead->pre; //  phead->pre->next原来尾结点的后继,node->pre = phead->pre->next这里出现了野指针的问题 phead->pre->next没有具体指向node->next = phead;//接下来在处理 原来尾结点,哨兵位/*,这样写是错的,以下2句话不可以颠倒已经把node这个结点给覆盖掉了,在执行65行代码时,node 是未知的,因为这个结点已经被覆盖了,phead->pre->next此时他的指向也就是未知的,调试时不会报错,但是遍历读取他就会报错了,就像你在打印函数里进行打印时,出现野指针phead->pre = node;phead->pre->next = node;*/phead->pre->next = node;phead->pre = node;//别忘了,每尾插进来的结点最终都是一个新的尾结点
}
void PushFront(ListNode* phead,DataType x)//头插
{//头插是在哨兵位的后面进行插入assert(phead);//为插入的数据开辟空间ListNode* node = ListBuyNode(x);//处理node 的后继,前驱node->pre = phead;node->next = phead->next;//处理phead ,phead->next /*这样写也是对的phead->next->pre = node;*/phead->next = node;//头节点要进行更新phead->next->pre = node;}
void PopBack(ListNode* phead)//尾删
{//注意不能直接删除,需要先找到尾结点的前一个结点assert(phead);//先判断以下是否为空,有2种写法/*if (phead->pre == phead){return 9;}*/assert(phead->next != phead);//是否为空ListNode* del = phead->pre;//把要删除的结点先保存起来//删除之前,先要构成一个新的双向循环链表del->pre->next = phead;phead->pre = del->pre;/*错误的,顺序不能颠倒,因为此时 del->pre已经被覆盖了phead->pre = del->pre;//新的尾结点del->pre->next = phead;*/free(del);del = NULL;}
void PopFront(ListNode* phead)//头删
{assert(phead);assert(phead->next != phead);//确保不为空ListNode* del = phead->next;//以下2句没有先后顺序之分del->next->pre = phead;phead->next = del->next;free(del);del = NULL;}
void InsertAfter(ListNode* pos, DataType x)//指定位置之后插入
{assert(pos);//为插入的数据开辟空间ListNode* node = ListBuyNode(x);//处理 node 的前驱,后继node->next = pos->next;node->pre = pos;//处理 pos 和 pos->next的prepos->next = node;pos->next->pre = node;}
void Earse(ListNode* pos)//指定位置删除
{assert(pos);//需要找到pos之前的一个结点和之后的一个结点pos->next->pre = pos->pre;pos->pre->next = pos->next;free(pos);pos = NULL;
}
ListNode* Find(ListNode* phead, DataType x)//在链表中按值查找,若是找到则返回对应的结点
{assert(phead);ListNode* pcur = phead->next;//定义一个用来循环遍历的指针while (pcur != phead){if (pcur->data == x){return pcur;//直接返回对应结点}pcur = pcur->next;}printf("没有找到\n");
}
void Destroy(ListNode* phead)//链表销毁;想一下,传一级指针还是二级?  在这里我们传入一级指针,为了保持接口一致性
{//销毁我们是一个一个进行删除,自然就需要遍历assert(phead);ListNode* del = phead->next;while (del != phead){ListNode* next = del->next;free(del);/*del = NULL;*/    //  ?del = next;}//来到这说明,此时只有一个哨兵位free(phead);phead = NULL;}
void LTDestroy(ListNode** pphead) 
{assert(pphead && *pphead);ListNode* cur = (*pphead)->next;while ( cur!=*pphead ){ListNode* next = cur->next;free(cur);cur = next;}free(*pphead);*pphead = NULL;}

 List.h对应完整代码

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdbool.h>#include<stdlib.h>
//双向链表的实现
typedef int DataType;
typedef struct ListNode {DataType data;//数据域struct ListNode* pre;//前驱域struct ListNode*next;//后继域
}ListNode;//接口函数的声明
//void Init(ListNode** pphead);//初始化,采用传参的形式
ListNode* Init();//初始化,采用不传参的形式,但是需把哨兵位这个结点返回
void Print(ListNode* phead);//链表打印
void PushBack(ListNode* phead,DataType x);//尾插,这里传入一级指针即可,因为返回的头节点我们不需要进行更改
void PushFront(ListNode* phead, DataType x);//头插
void PopBack(ListNode* phead);//尾删
void PopFront(ListNode* phead);//头删
void InsertAfter(ListNode* pos,DataType x);//指定位置之后插入
void Earse(ListNode* pos);//指定位置删除
ListNode* Find(ListNode*phead,DataType x);//按值查找,若是找到则返回对应的结点
void Destroy(ListNode* phead);//链表销毁;想一下,传一级指针还是二级?为了保持接口一致性我们传入一级指针

ok,以上就是我要为大家进行share的一些基本内容,都来到这里了,要是感觉我写的还不错的话,各位大佬烦劳点个赞,互关以下呗~~~

相关文章:

双向链表的初步练习

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇: Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”…...

IDE的组成

集成开发环境&#xff08;IDE&#xff0c;Integrated Development Environment &#xff09;是用于提供程序开发环境的应用程序&#xff0c;一般包括代码编辑器、编译器、调试器和图形用户界面等工具。集成了代码编写功能、分析功能、编译功能、调试功能等一体化的开发软件服务…...

项目解读_v2

1. 项目介绍 如果使用task2-1作为示例时&#xff0c; 运行process.py的过程中需要确认 process调用的是函数 preprocess_ast_wav2vec(wav, fr) 1.1 任务简介 首个开源的儿科呼吸音数据集&#xff0c; 通过邀请11位医师标注&#xff1b; 数字听诊器的采样频率和量化分辨率分…...

杀毒软件哪个好,杀毒软件有哪些

安全杀毒软件是一种专门用于检测、防止和清除计算机病毒、恶意软件和其他安全威胁的软件。这类软件通常具备以下功能&#xff1a; 1. 实时监测&#xff1a;通过实时监测计算机系统&#xff0c;能够发现并防止病毒、恶意软件等安全威胁的入侵。 2. 扫描和清除&#xff1a;可以…...

Ubuntu上安装配置Nginx

要在 Ubuntu 上安装 Nginx&#xff0c;请按照以下步骤进行操作&#xff1a; 打开终端&#xff1a;可以使用快捷键 Ctrl Alt T 打开终端&#xff0c;或者在开始菜单中搜索 “Terminal” 并点击打开。 更新软件包列表&#xff1a;在终端中运行以下命令&#xff0c;以确保软件包…...

C++之string

C之string #include <iostream>using namespace std;/*string();//创建一个空的字符串string(const char* s);//使用字符串s初始化string(const string& str);//使用一个string对象初始化另外一个string对象string(int n,char c);//使用n个字符c初始化*/void test1()…...

多线程---单例模式

文章目录 什么是单例模式&#xff1f;饿汉模式懒汉模式版本一&#xff1a;最简单的懒汉模式版本二&#xff1a;考虑懒汉模式存在的线程安全问题版本三&#xff1a;更完善的解决线程安全问题版本四&#xff1a;解决指令重排序问题 什么是单例模式&#xff1f; 单例模式&#xf…...

SpringBoot相比于Spring的优点(自动配置和依赖管理)

自动配置 例子见真章 我们先看一下我们Spring整合Druid的过程&#xff0c;以及我们使用SpringBoot整合Druid的过程我们就知道我们SpringBoot的好处了。 Spring方式 Spring方式分为两种&#xff0c;第一种就是我们使用xml进行整合&#xff0c;第二种就是使用我们注解进行简化…...

SAP SPAD新建打印纸张

SAP SPAD新建打印纸张 1.事务代码SPAD 2.完全管理&#xff0d;设备类型&#xff0d;页格式-显示(创建格式页) 3.按标准A4纸张为模板参考创建。同一个纸张纵向/横向各创建1次(创建格式页) 4.完全管理&#xff0d;设备类型&#xff0d;格式类型-显示(创建格式类型&#xff0…...

C# 图解教程 第5版 —— 第11章 结构

文章目录 11.1 什么是结构11.2 结构是值类型11.3 对结构赋值11.4 构造函数和析构函数11.4.1 实例构造函数11.4.2 静态构造函数11.4.3 构造函数和析构函数小结 11.5 属性和字段初始化语句11.6 结构是密封的11.7 装箱和拆箱&#xff08;*&#xff09;11.8 结构作为返回值和参数11…...

车载电子电器架构 —— 基于AP定义车载HPC

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…...

Redis原理-IO模型和持久化

高性能IO模型 为什么单线程Redis能那么快 一方面&#xff0c;Redis 的大部分操作在内存上完成&#xff0c;再加上它采用了高效的数据结构&#xff0c;例如哈希表和跳表&#xff0c;这是它实现高性能的一个重要原因。另一方面&#xff0c;就是 Redis 采用了多路复用机制&#…...

PID控制示例

PID控制简单示例 import numpy as np import matplotlib.pyplot as plt import copy# 定义曲线函数 y sin(x) def target_curve(x):return np.sin(x)class PID:def __init__(self, kp, ki, kd):self.kp kpself.ki kiself.kd kdself.ep 0.0self.ei 0.0self.ed 0.0self.d…...

GoLand GC(垃圾回收机制)简介及调优

GC(Garbage Collector)垃圾回收机制及调优 简单理解GC机制 其实gc机制特别容易理解&#xff0c;就是物理内存的自动清理工。我们可以把内存想象成一个房间&#xff0c;程序运行时会在这个房间里存放各种东西&#xff0c;但有时候我们会忘记把不再需要的东西拿出去&#xff0c…...

AI:40-基于深度学习的森林火灾识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌本专栏包含以下学习方向: 机器学习、深度学…...

37基于MATLAB平台的图像去噪,锐化,边缘检测,程序已调试通过,可直接运行。

基于MATLAB平台的图像去噪&#xff0c;锐化&#xff0c;边缘检测&#xff0c;程序已调试通过&#xff0c;可直接运行。 37matlab边缘检测图像处理 (xiaohongshu.com)...

通过Metasploit+Ngrok穿透内网长期维持访问外网Android设备

前言: 因为之前作为小白我不会在Kali Linux里面把IP映射出外网&#xff0c;卡在那个地方很久&#xff0c;后来解决了这个问题就写方法出来和大家分享分享。 环境&#xff1a; Kali Linux系统(https://www.kali.org/downloads/) Metasploit Ngrok Linux64位的端口转发工具(htt…...

STM32 CubeMX配置USB HID功能,及安装路径

STM32CubeMX学习笔记&#xff08;46&#xff09;——USB接口使用&#xff08;HID自定义设备&#xff09; STM32CubeMX实现STM32 USBHID双向64字节通信(下位机部分) STM32 USB HID设置(STM32CubeMX) 关于keil 5安装出现Fail to set path to Software Packs.问题解决方法...

【错误解决方案】ModuleNotFoundError: No module named ‘transformers‘

1. 错误提示 在python程序中&#xff0c;尝试导入一个名为transformers的模块&#xff0c;但Python提示找不到这个模块。 错误提示&#xff1a;ModuleNotFoundError: No module named ‘transformers‘ 2. 解决方案 所遇到的问题是Python无法找到名为transformers的模块&am…...

Mac 配置环境变量

Mac 配置环境变量 修改配置文件 vim ~/.bash_profile i进入编辑模式. Esc&#xff1a;wq 保存文件 esc:q 退出 如&#xff1a;jdk环境变量配置 JAVA_HOME/Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home CLASSPATH J A V A H O M E / l i b / t o o l…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...