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

【数据结构】堆(挑战从零基础到进阶)

 我们从概念开始一步步来学习堆,下面我们来从零基础来解剖该种数据结构。先提前透明:实现堆其实就是基于数组来实现一个完全二叉树而已 !


目录

堆的概念

堆的性质

堆的物理逻辑&思维逻辑

堆的节点对应关系

堆的核心操作

(1)堆的数组结构

(2)堆的初始化操作

(3)堆的插入节点

  ●堆的插入:上浮调整

(4)堆的删除节点

  ●堆的删除:下沉调整

(5)堆的释放

堆的常见运用

堆的完整代码+思维逻辑


堆的概念

堆(Heap)是数据结构中一种特殊的计算机结构,是一种特殊的完全二叉树,为非线性的数据结构,一般用于优先队列、堆排序、Top-k的问题等等。下面是对完全二叉树的一个简单学习,有疑惑的朋友们可以看我的上一篇文章,里面详细的讲解了相关概念:【数据结构】二叉树

完全二叉树是二叉树中的一种,它的节点满足以下规律,有且只有以下三种情况:

1:有左右两个孩子     2:只有左孩子   3:没有孩子(叶子节点)

叶子节点只可能在最大的两层出现且在最左层的叶子节点都集中在左侧。如下图理解:

注意:除了最后一层,如果每个父节点的孩子节点都有两个,它属于满二叉树,注意区别! 

堆的性质

结构特性:从上面我们可以观察到,除了一层外,其它层是满层的,最后一层居左对齐

堆序性质:可以看到,它的节点关键字是有序的,因此我们又可以将堆分为两类:

可以观察到大顶堆中,它的根节点关键字是整个树中是最大的,而每个父节点的孩子节点都要小于它的父节点。在小顶堆中,整个树的根节点关键字最小,且每个父节点关键字小于孩子节点的关键字。除此之外,有一个要补充的点:除了根节点的关键字处于两个极端之外,父节点的关键字是可以等于它的孩子节点的。下面我们进行精髓总结,方便记忆:

大顶堆:父节点的值>=子节点的值(根节点在整个树中最大)

小顶堆:父节点的值<=子节点的值(根节点在整个树中最小)

堆的物理逻辑&思维逻辑

咱们知道,堆是一种完全二叉树,但是我们选择的是数组方式的存储,没有用链表,可能有人想:如果我用数组实现堆,那么它的下标是不是就乱序了?并非如此,我们看下面这幅图:

我们的堆元素是依次存储到数组里面的,比如根节点对应数组下标1,根节点的左子节点对应数组的 2,根节点的右子节点对应数组的3,依次类推满足:从上到下,从左到右。思维逻辑方便我们在查找问题,以及写各种操作时借助二叉树来直观的体现,如果我们在数组上操作进行思维操作,那么会很乱,大家理解了吗!

堆的节点对应关系

因为我们是数组实现,节点下标之间的索引存在固定的数学关系:

如果堆在数组中的下标是从0开始,那么满足:

假如父节点在数组的下标为 i 它的左孩子节点在数组的下标为 2 * i+1,右节点数组下标为:2*i+2

如果堆在数组中的下标是从1开始,那么满足:

假如父节点在数组的下标为 i 它的左孩子节点在数组的下标为 2*i,右节点数组下标为:2*i+1

假如子节点在数组的下标为 k 它的父节点在数组中的下标为(k-1)/2

例如:

在左边的大顶堆中, 根节点在数组中的下标为1,那么它的左子节点在数组中的下标按照公式得出2*1=2,右孩子节点的数组下标为2*1+1=3,反过来,它的子节点的数组下标为3,它的根节点(父节点)数组下标为(3-1)/2=1。右边的小顶堆也是同理!

在右边的小顶堆中,根节点的数组下标从0开始 ,它的根节点(父节点)下标为0,那么它的左子节点下标按照公式为2*0+1=1,它的右子节点数组下标为2*0+2=2,反过来它的左子节点数组下标为1,那么它的父节点数组下标按照公式为(1-1)/2=0,同理,大顶堆也是如此!

堆的核心操作

首先我们需要用数组的形式来模拟完全二叉树,来依次完成下面的操作,时间复杂度都是logN

初始化操作:初始化我们要开辟一定的空间,并返回这个指向这个空间的指针 

插入:向整个树中添加一个元素

           (1)上浮:进行调整新插入的元素路径

删除:不同的删除操作之后我们需要保证余下的元素满足堆的规则,因此需要下面两个操作:

           (1)下沉:调整删除后的堆顶元素路径

堆的销毁:我们会进行动态开辟,因此养成良好习惯,及时释放开辟的空间

上浮与下沉的作用说的通俗一些就是每次进行插入与删除操作后,通过它们来调节堆的节点,使它们满足堆的性质

(1)堆的数组结构

堆的数组结构,需要一个计算当前存储量的,还有一个指向动态数组空间指针,以及最大存储个数

下面我以根节点下标为0的大顶堆存储来进行举例!

#define MAX 10typedef struct Heap
{int size;//当前元素个数int MAX_size;//最大存储个数int* data;//动态存储空间
}Heap;
(2)堆的初始化操作

 初始化我们需要只需要让结构体里面的指针指向一片空间就可以,成员进行一些初始化设置,注意我们在这里实现的堆下标是从0开始,所以size初始化为-1。有的堆是从下标1开始,记得区分!

//初始化
void Preliminary(Heap*Newnode)
{Newnode->data = (Heap*)malloc(sizeof(Heap) * MAX);//空间有效性的判断if (Newnode->data == NULL){printf("空间开辟无效\n");return;}//初始化内容Newnode->size = -1;Newnode->MAX_size = MAX;
}
(3)堆的插入节点

在插入节点之前:我们应该判断空间是否支持插入,这里面涉及了判断NULL以及空间大小判断

在插入节点之后:因为我们写的是大顶堆,因此需要对刚插入的成员根据情况是否进行调整位置

按照大顶堆的规则,根节点的关键字应该是最大的,保证每个父节点都要比它的孩子节点大,同时为了以后方便维护我们需要另外写两个函数:上浮调整函数、交换函数 

//插入
void Inport(Heap* Newnode, int data)
{//判断空间是否存在if (Newnode == NULL){printf("空间无效\n");return;}//判断是否是满空间if (Newnode->size == Newnode->MAX_size){int * pc = (int *)realloc(Newnode->data, sizeof(Heap) * Newnode->size * 2);if (pc == NULL){printf("扩容失败\n");return;}//改变当前最大存储量Newnode->MAX_size += Newnode->MAX_size;//连接Newnode->data = pc;printf("扩容成功\n");}Newnode->size++;Newnode->data[Newnode->size] = data;//上浮调整Adjust(Newnode->data, Newnode->size);
}
  ●堆的插入:上浮调整

我们将这个新插入孩子节点下标传过去,注意是传址操作!然后算他的父节点下标位置,在用while循环来一直进行判断是否需要调整这个孩子节点和它的父子节点,这里需要理解循环的条件,已经调整完之后需要进行的两个操作,请看下面的代码:

//上浮调整
void Adjust(Heap* Newnode, int child)
{//计算父节点的关键字int parent = (child - 1) / 2;//判断是否需要交换while (child > 0){if (Newnode->data[child] > Newnode->data[parent]){//满足条件就交换Exchange(&Newnode->data[child], &Newnode->data[parent]);child = parent;parent = (child - 1) / 2;}elsebreak;}
}
//交换
void Exchange(Heap* p1, Heap* p2)
{Heap x = *p1;*p1 = *p2;*p2 = x;
}
(4)堆的删除节点

删除堆中的某个节点,咱们如果直接删除尾部的孩子节点,并没有什么意义,所以咱们来进行删头 ,也就是删除根节点,我们原先已经将堆按照大顶堆规则排列,那么我们如果删除头,取的是整个堆的最大元素,这非常适合排序!(时间复杂度是logN

●堆的删除:下沉调整

那么重点来了:如果我们删除根节点,那么再去一个个摞另外的元素重新排列吗?不,这样效率很低。下面介绍一个方法:将根节点与最后一个孩子节点交换位置,然后元素个数size减一,接下来我们先说重新调整的一种方法:下沉 

上面是第一步:先交换两个元素,同时size减一

//删除
void Disposal(Heap* Newnode)
{//断言空指针assert(Newnode);//交换根节点与最后一个下标的孩子节点Exchange(&Newnode->data[0], &Newnode->data[Newnode->size]);Newnode->size--;//下沉调整Underneath(Newnode->data,Newnode->size);
}

下面进行最后一步:循环比较此时的根节点和它的孩子节点,哪个大交换哪个,如果小于孩子节点,就结束循环,下面是代码参照

//下沉调整
void Underneath(int *data,int size)
{int parent = 0;int child = 2 * parent + 1;//比较父节点(根节点)与它的最大的孩子节点while (child < size){//先避免越界,再比较两个孩子节点,找最大的if (child < size && data[child] < data[child+1]){++child;}if (data[child] > data[parent]){Exchange(&data[child], &data[parent]);//改变父节点下标parent = child;child = 2 * parent + 1;}else{break;}}
}
(5)堆的释放

咱们可以先释放元素,再释放开辟好的空间,这里注意释放的指针必须是指向空间的起始位置!

//堆的释放
void Free(Heap *Newnode)
{assert(Newnode);int num = 0;while (num <= Newnode->size){Newnode->data[num] = 0;num++;}//释放指针int* Node = Newnode->data;free(Node);Node = NULL;printf("释放成功");
}

堆的常见运用

1:优先队列

这是一种特殊的数据结构,它的排列是顺序是按照元素的优先级的,注意不是插入的顺序。它可以在O(1)的时间复杂度内获取当前优先级最高的元素,它的删除与插入与堆一样都是logN。堆结构可以快速实现插入和删除操作,采用的数组又降低了难度。

2:Top K问题

Top K问题就是从一堆数据中找到前K个最大、最突出的元素,这点是不是很适合堆!?比如在一堆杂乱的元素中找到前4个最大的元素,这里刚好可以借助调整的上浮、下沉操作来实现

3:堆排序

核心思想就是利用父节点优先级高于子节点的特性,将无序数据排序成有序数据,这点直接用堆的调整函数就可以实现!

堆的完整代码+思维逻辑

首先咱们定义了一个结构体,这个结构体里面肯定有一个指向动态空间的指针,接下来肯定要涉及动态开辟已经初始化操作。随之就是给这个空间存储数据,这个过程也很简单,因为咱们得底层逻辑还是数组,只需要按照下标一个个存进去就可以。之后考虑到存进去的元素不符合大顶堆的规定,我们需要写一个调整函数,每次放入一个值需要与它的父节点比较,看是否交换位置,来实现初步的堆。最后是删除操作,咱们删除堆的堆顶,删除之后考虑到如果移动整个堆,那样就会很麻烦,因此我们使用特殊的删除方法:堆尾与堆头互换,然后删除堆尾,也就是下标减一。其次就是重新排序,我们只需要取整个堆的最大值放在堆头即可。以上就是整个思维逻辑!

#define _CRT_SECURE_NO_WARNINGS 1
#include"text.h"int main()
{int data = 0;Heap Newnode;//初始化Preliminary(&Newnode);//插入for (int i = 10; i < 110; i += 10){data = i;Inport(&Newnode, data);}//删除Disposal(&Newnode);//打印Printf_t(Newnode);//堆的释放Free(&Newnode);return 0;
}

头文件:

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>#define MAX 10typedef struct Heap
{int size;//当前元素个数int MAX_size;//最大存储个数int* data;//动态存储空间
}Heap;//初始化
void Preliminary(Heap*Newnode);
//上浮调整
void Adjust(int* data, int child);
//插入
void Inport(Heap* Newnode, int data);
//下沉调整
void Underneath(int* data, int size);
//交换
void Exchange(int* p1, int* p2);
//删除
void Disposal(Heap* Newnode);
//打印
void Printf_t(Heap Newnode);
//堆的释放
void Free(Heap *Newnode);

函数实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include"text.h"//初始化
void Preliminary(Heap * Newnode)
{Newnode->data = (Heap*)malloc(sizeof(Heap) * MAX);//空间有效性的判断if (Newnode->data == NULL){printf("空间开辟无效\n");return;}//初始化内容Newnode->size = -1;Newnode->MAX_size = MAX;
}//交换
void Exchange(int* p1, int* p2)
{int x = *p1;*p1 = *p2;*p2 = x;
}//上浮调整
void Adjust(int* data, int child)
{//计算父节点的关键字int parent = (child - 1) / 2;//判断是否需要交换while (child > 0){if (data[child]>data[parent]){//满足条件就交换Exchange(&data[child], &data[parent]);child = parent;parent = (child - 1) / 2;}elsebreak;}
}//插入
void Inport(Heap* Newnode, int data)
{//判断空间是否存在if (Newnode == NULL){printf("空间无效\n");return;}//判断是否是满空间if (Newnode->size == Newnode->MAX_size){int * pc = (int *)realloc(Newnode->data, sizeof(Heap) * Newnode->size * 2);if (pc == NULL){printf("扩容失败\n");return;}//改变当前最大存储量Newnode->MAX_size += Newnode->MAX_size;//连接Newnode->data = pc;printf("扩容成功\n");}Newnode->size++;Newnode->data[Newnode->size] = data;//上浮调整Adjust(Newnode->data, Newnode->size);
}//下沉调整
void Underneath(int *data,int size)
{int parent = 0;int child = 2 * parent + 1;//比较父节点(根节点)与它的最大的孩子节点while (child < size){//先避免越界,再比较两个孩子节点,找最大的if (child < size && data[child] < data[child+1]){++child;}if (data[child] > data[parent]){Exchange(&data[child], &data[parent]);//改变父节点下标parent = child;child = 2 * parent + 1;}else{break;}}
}//删除
void Disposal(Heap* Newnode)
{//断言空指针assert(Newnode);//交换根节点与最后一个下标的孩子节点Exchange(&Newnode->data[0], &Newnode->data[Newnode->size]);Newnode->size--;//下沉调整Underneath(Newnode->data,Newnode->size);
}//打印
void Printf_t(Heap Newnode)
{if (Newnode.size == 0){printf("没有元素,无法打印\n");return;}int num = 0;printf("堆元素:");while (num <= Newnode.size){printf("%d ", Newnode.data[num]);num++;}printf("\n");
}
//堆的释放
void Free(Heap *Newnode)
{assert(Newnode);int num = 0;while (num <= Newnode->size){Newnode->data[num] = 0;num++;}//释放指针int* Node = Newnode->data;free(Node);Node = NULL;printf("释放成功");
}

                                                         拿走不谢!记得三连!

相关文章:

【数据结构】堆(挑战从零基础到进阶)

我们从概念开始一步步来学习堆&#xff0c;下面我们来从零基础来解剖该种数据结构。先提前透明&#xff1a;实现堆其实就是基于数组来实现一个完全二叉树而已 &#xff01; 目录 堆的概念 堆的性质 堆的物理逻辑&思维逻辑 堆的节点对应关系 堆的核心操作 &#xff08…...

阿里 DataWorks:数据治理、安全中心、数据质量核心功能梳理

文章目录 阿里 DataWorks&#xff1a;云原生数据治理与安全一体化实践指南一、数据治理中心1.1 数据治理体系1.2 产品架构全图1.3 概要使用路径1.4 治理现状评估数据治理健康分评估模型 1.5 检查项 & 治理项(1) 检查项(2) 治理项 1.6 治理工具箱1.7 治理结果查看 二、安全中…...

DeepSeek精品课分享 清北

AI淘金潮上热搜&#xff01;有人已经靠DeepSeek日入过万了&#xff01; 北大清华等高校也相继出品DeepSeek高质量精品课程&#xff0c;在网上传疯。帮助学者高效学习AI从入门到精通&#xff01; 完整版资料已经帮大家整理好了&#xff0c;免费领&#xff01; 资料链接: htt…...

获取当前页面的 url 参数

一、使用 URLSearchParams&#xff08;现代浏览器支持&#xff09; URLSearchParams 是 JavaScript 提供的一个内置对象&#xff0c;用于处理 URL 的查询字符串&#xff0c;它提供了一系列方便的方法来获取、设置和删除查询参数。 // 获取当前页面的 URL 参数 const queryStr…...

解锁健康密码,开启养生之旅

在这个生活节奏飞快、压力如影随形的时代&#xff0c;健康养生不再是一句空泛的口号&#xff0c;而是我们对高品质生活的热切追求。它就像一把神奇的钥匙&#xff0c;能够打开通往活力与幸福的大门。 日常习惯与养生息息相关。晨起后&#xff0c;空腹喝一杯温水&#xff0c;如…...

Mybatis集合嵌套查询,三级嵌套

三个表&#xff1a;房间 玩家 玩家信息 知识点&#xff1a;Mybatis中级联有关联&#xff08;association&#xff09;、集合&#xff08;collection&#xff09;、鉴别器&#xff08;discriminator&#xff09;三种。其中&#xff0c;association对应一对一关系、collectio…...

Python的Pandas和matplotlib库:让数据可视化贼简单

在数据爆炸的时代&#xff0c;数据可视化已成为数据分析的关键环节。Python 作为强大的编程语言&#xff0c;拥有众多用于数据可视化的库&#xff0c;而 pandas 库在其中扮演着重要角色。它不仅能高效处理和分析数据&#xff0c;还具备强大的数据可视化功能&#xff0c;让我们轻…...

数据结构基础之《(19)—矩阵处理》

一、zigzag打印矩阵 Z字形打印矩阵 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 打印顺序&#xff1a;1,2,7,13,8,3,4,9,14... 核心技巧&#xff1a;找到coding上的宏观调度 左上角有A、B两个点&#xff0c;A往右一步一步走&#xff0c;B往下一步一步走 写一个…...

IDEA与Maven使用-学习记录(持续补充...)

1. 下载与安装 以ideaIU-2021.3.1为例&#xff0c;安装步骤&#xff1a; 以管理员身份启动ideaIU-2021.3.1修改安装路径为&#xff1a;D:\Program Files\JetBrains\IntelliJ IDEA 2021.3.1勾选【创建桌面快捷方式】&#xff08;可选&#xff09;、【打开文件夹作为项目】&…...

【运维笔记】docker 中 MySQL从5.7版本升级到8.0版本 - 平滑升级

在Docker环境中&#xff0c;将MySQL从5.7版本升级到8.0版本时&#xff0c;数据确实需要迁移。虽然不能直接通过docker-compose命令简单地进行版本升级并保留所有数据&#xff0c;但可以通过一系列步骤来实现平滑升级和数据迁移。以下是详细的迁移步骤&#xff1a; 一、准备阶段…...

nuxt2 打包优化使用“compression-webpack-plugin”插件

在使用 Nuxt.js 构建项目时&#xff0c;为了提高性能&#xff0c;通常会考虑对静态资源进行压缩。compression-webpack-plugin 是一个常用的 Webpack 插件&#xff0c;用于在生产环境中对文件进行 Gzip 压缩。这对于减少网络传输时间和提高页面加载速度非常有帮助。下面是如何在…...

fastapi+mysql实现增删改查

说明&#xff1a; 我计划用python的fastapi框架&#xff0c;实现操作MySQL数据库的表&#xff0c;实现增删改查的操作&#xff0c;并且在postman里面测试 step1: 安装数据库依赖 pip install fastapi uvicorn pymysqlstep2:C:\Users\Administrator\PycharmProjects\FastAPIPro…...

ArcGIS Pro:轻松制作地震动画,洞察灾害动态

在当今的信息展示领域&#xff0c;动画因其直观、生动的特点&#xff0c;逐渐成为各类汇报、研究展示中的重要元素。 尤其是在地理信息领域&#xff0c;通过动画来展示动态的地理现象&#xff0c;能够让观众更清晰地理解数据背后所蕴含的信息。 地震作为一种突发性的自然灾害…...

Redis系列之慢查询分析与调优

Redis 慢查询分析与优化&#xff1a;提升性能的实战指南 Redis 作为一款高性能的内存数据库&#xff0c;因其快速的数据读写能力和灵活的数据结构&#xff0c;被广泛应用于缓存、消息队列、排行榜等多种业务场景。然而&#xff0c;随着业务规模的扩大和数据量的增加&#xff0…...

Linux下磁盘读写流

用户空间请求 系统调用&#xff1a;应用程序通过系统调用&#xff08;如 read() 或 write()&#xff09;请求对文件或设备进行读写操作。 文件描述符&#xff1a;操作通过文件描述符进行&#xff0c;该描述符在应用程序打开文件时获得。 VFS&#xff08;虚拟文件系统&#xff…...

MAVEN手动配置(阿里云)全教程

介于网上各种各样的MAVEN配置过程中方法大致相同却细节参差不齐&#xff0c;我总结了我遇见的一些问题&#xff0c;来完全的解决MAVEN手动配置的全过程&#xff0c;以及分享解决小毛病的经验。 所需材料&#xff1a; MAVEN3.9.9&#xff08;下载适合自己的版本即可&#xff09…...

贪心算法一

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;了解什么是贪心算法&#xff0c;并且掌握贪心算法。 > 毒鸡汤&#xff1a;有些事情&#xff0c;总是不明白&#xff0c;所以我不会坚持。早安! >…...

计算机毕业设计Python+DeepSeek-R1大模型微博的话题博文及用户画像分析系统 微博舆情可视化(源码+ 文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

Android Framework 常见面试题

以下是常见的 Android Framework 面试题&#xff0c;涵盖基础到高级知识点&#xff0c;帮助准备面试&#xff1a; 一、基础问题 Android 系统架构分层 描述 Android 系统的四层架构&#xff08;Linux Kernel、Native、Framework、Application&#xff09;及各层职责。 Zygote …...

绕过某书frida反调试检测 获取某宝支付参数

前言 在移动应用安全测试和研究过程中,我们经常需要使用Frida等工具对应用进行动态分析。然而,很多应用都实现了反调试和反注入机制,用来检测并阻止此类分析工具的使用。本文将分享如何使用Frida绕过某流行阅读应用(以下简称"某书",本次任务目的原本是需要找出…...

Secret Cow Code S

归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍+ 收藏⭐ + 留言​📝 既然选择了远方,当不负青春,砥砺前行! 题目描述 奶牛们正在实验秘密代码,并设计了一种方法用于生成无限长度的字符…...

MyBatis - XML CRUD 其他查询

1. XML 配置文件 使用 MyBatis 操作数据库的方式有两种: 注解 (在注解中定义 SQL 语句)XML 配置文件 (在 XML 文件中定义 SQL 语句) 在上一篇博客中, 已经讲解了如何使用注解操作数据库, 本篇文章来讲解如何使用 XML 进行 MyBatis 开发. 使用 XML 的步骤, 和使用注解的步骤…...

牛客python蓝桥杯11-32(自用)

11 import os import sysdef huiwen(str):length len(str)# if length 0:# return -1result []for i in range(length-1): # 0 - length-2for j in range(i2,length1):# 取出从索引 i 到 j-1 的子串s str[i:j]# 正序倒序if s s[::-1]:result.append(len(s))if result…...

rabbitmq版本升级并部署高可用

RabbitMQ版本升级 先检查是否已经安装rabbitmq rpm -qa|grep rabbitmq|wc -l //如果结果是0&#xff0c;表示没有安装 rpm -e --nodeps $(rpm -qa|grep rabbitmq) //如安装了&#xff0c;则进行卸载 先检查是否已经安装erlang rpm -qa|grep erlang|wc -l //如果结果…...

Spring AI 1.0.0-M6 快速开始(一)

Spring AI 1.0.0-M6 入门一、存储库二、依赖管理完整maven 入门 Spring 是JAVA中我们经常使用的框架之一&#xff0c;Spring AI不断的发展迭代目前已经到M6版本据说上半年会出一个稳定版本。 本节提供了如何开始使用Spring AI的M6。 一、存储库 1.0 M6 -添加Spring存储库 需…...

数据集路径出错.yaml‘ images not found , missing path

方法一&#xff1a;删除settings.yaml 方法二&#xff1a;dataset_name.yaml改用绝对路径&#xff0c;如最后一张图 错误分析&#xff1a; dataset_name.yaml中的path的路径仅支持绝对路径&#xff0c;写相对路径就会搜索不到&#xff0c;使用settings.json中的路径&#xff0…...

win32汇编环境,对话框中使用树形视图示例二

;运行效果 ;win32汇编环境,对话框中使用树形视图示例二 ;得到树形视图控件Treeview的全路径字符串,这里的方法是由子项向父项挨个找的算法找齐路径 ;直接抄进RadAsm可编译运行。重要部分加备注。 ;下面为asm文件 ;>>>>>>>>>>>>>>&g…...

二维码(带背景图片或动态图)

原文链接&#xff1a;http://www.juzicode.com/archives/6377 桔子菌逛github的时候发现一个好玩的Python库&#xff0c;可以用来制作带背景图片或动态图的二维码&#xff0c;这个库在pypi上也有发布&#xff0c;可以直接通过pip安装&#xff1a; python -m pip install amzq…...

【连珠云弈】网页五子棋版项目测试报告

目录 一、项目背景 1.1、项目起源 1.2、市场需求 1.3、项目目标 二、项目功能 2.1 用户管理功能 2.2 游戏对战功能 三、测试报告 3.1.功能测试 ​编辑 3.1.1注册功能测试 解决bug&#xff1a; 测试总结&#xff1a; 3.1.2登录功能测试 测试总结&#xff1a; 3.…...

OpenCV计算摄影学(15)无缝克隆(Seamless Cloning)调整图像颜色的函数colorChange()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::colorChange 是 OpenCV 中用于调整图像颜色的函数。它允许你通过乘以不同的系数来独立地改变输入图像中红色、绿色和蓝色通道的强度&#xf…...