文章目录 深入分析 Android BroadcastReceiver (二)1. 深入理解 BroadcastReceiver 的高级使用和优化2. 有序广播(Ordered Broadcasts)2.1 实现有序广播 3. 粘性广播(Sticky Broadcasts)3.1 使用粘性广播 4. 本地广播(LocalBroadcastManager)4.1 使用本地广播 5. 安全性与权限管理5.1 声明权限 6. 性能优化6.1 避免耗时操作6.2 动态注册和取消注册6.3 使用本地广播 7. 实战示例7.1 本地广播接收器7.2 动态注册和取消注册7.3 有序广播接收器7.4 AndroidManifest.xml 中声明 8. 总结 深入分析 Android BroadcastReceiver (二) 1. 深入理解 BroadcastReceiver 的高级使用和优化 BroadcastReceiver 是 Android 中用于接收广播消息的重要组件。通过对 BroadcastReceiver 的高级使用和优化,开发者可以实现更加高效、灵活的应用逻辑。
2. 有序广播(Ordered Broadcasts) 有序广播允许多个接收器按照优先级顺序接收广播,并且可以中断广播的传播。
2.1 实现有序广播 发送有序广播: Intent intent = new Intent("com.example.ORDERED_ACTION"); sendOrderedBroadcast(intent, null); 接收有序广播: 在 AndroidManifest.xml 中声明接收器,并设置优先级。
<receiver android:name=".OrderedReceiver" android:priority="
Hadoop框架讲解 Hadoop是一个开源的分布式计算框架,广泛应用于大数据处理和分析领域。它提供了一个可靠、可扩展和高效的方式来处理大规模数据集。本文将介绍Hadoop的基本概念、核心组件和使用方法。
目录 Hadoop简介Hadoop核心组件Hadoop生态系统Hadoop安装与配置Hadoop使用示例总结 Hadoop简介 Hadoop由Apache Software Foundation开发,是一个用于存储和处理大数据的开源框架。它能够在计算机集群上分布式存储和处理大量数据,并具备高容错性和高可扩展性。Hadoop最初由Doug Cutting和Mike Cafarella开发,并以《指环王》中的角色“哈比人”(Hobbit)命名。
Hadoop的特点 分布式存储:将数据分布存储在集群的各个节点上。分布式计算:通过MapReduce编程模型并行处理数据。高容错性:数据在多个节点上进行复制,以保证数据的可靠性。高可扩展性:可以通过增加节点来扩展集群的存储和计算能力。 Hadoop核心组件 Hadoop框架主要包括两个核心组件:
2.1 Hadoop分布式文件系统(HDFS) HDFS是一个分布式文件系统,负责将数据分块存储在集群中的不同节点上。HDFS具有以下特点:
高容错性:数据块会在集群的多个节点上进行复制。高吞吐量:适合大规模数据集的批处理。流数据访问:支持以流式的方式读取数据。 HDFS架构由NameNode和DataNode组成:
NameNode:负责管理文件系统的元数据(如文件路径、数据块位置等)。DataNode:负责存储实际的数据块。 2.2 MapReduce MapReduce是一种分布式计算模型,负责在集群上并行处理大规模数据集。MapReduce编程模型包括两个主要阶段:
Map阶段:将输入数据分割成独立的块,并由Map函数处理生成中间结果。Reduce阶段:将中间结果进行合并处理,生成最终输出结果。 Hadoop生态系统 Hadoop生态系统包括一系列开源项目,用于扩展Hadoop的功能。这些项目包括但不限于:
YARN(Yet Another Resource Negotiator):资源管理和调度框架。Hive:基于Hadoop的数据仓库,用于查询和管理大规模数据集。Pig:数据流处理语言和执行框架。HBase:分布式NoSQL数据库,基于HDFS构建。Spark:快速、通用的大数据处理引擎。Sqoop:用于在Hadoop和关系型数据库之间传输数据的工具。Flume:用于收集、聚合和移动大数据的分布式服务。 Hadoop安装与配置 4.1 安装前准备 在安装Hadoop之前,需要确保以下条件:
安装Java环境(Hadoop依赖Java)。配置SSH无密码登录(用于节点间通信)。 4.2 下载和安装Hadoop 下载Hadoop:
访问 Apache Hadoop官网 下载Hadoop二进制文件。 解压Hadoop:
tar -xzvf hadoop-x.y.z.tar.gz 配置Hadoop环境变量: 在~/.bashrc文件中添加以下内容:
export HADOOP_HOME=/path/to/hadoop export PATH=$PATH:$HADOOP_HOME/bin export JAVA_HOME=/path/to/java 配置Hadoop核心文件: 编辑$HADOOP_HOME/etc/hadoop/core-site.xml:
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:9000</value> </property> </configuration> 编辑$HADOOP_HOME/etc/hadoop/hdfs-site.xml:
<configuration> <property> <name>dfs.replication</name> <value>1</value> </property> </configuration> 格式化HDFS文件系统: hdfs namenode -format 启动Hadoop服务: start-dfs.
在Java开发的世界中,我们会经常听到JDK、JRE和JVM这三个词。它们都与Java的运行环境以及Java程序的编译和运行有关,它们之间也存在一些关联性和区别。
什么是JDK、JRE和JVM
我们来看它们分别是什么。
JDK,全称Java Development Kit,即Java开发工具包。顾名思义,JDK是用于Java开发的一套工具包,里面包含了Java的编译器javac、Java程序打包工具jar、Java程序运行环境JRE、文档生成工具javadoc以及很多用于开发的工具,如调试工具jdb等。
JRE,全称Java Runtime Environment,即Java运行环境。JRE是运行Java程序所需的环境,包括JVM以及Java类库等。JRE是Java程序运行的实施场所,同时也提供了运行Java程序所必需的库文件。
JVM,全称Java Virtual Machine,即Java虚拟机。JVM是Java运行环境的核心,它负责Java程序的运行。JVM是一个虚拟的计算机,它接收到字节码(编译后的Java程序),然后解释或编译执行。
JDK、JRE和JVM之间的关系
JDK、JRE和JVM之间是什么关系呢?可以这么理解:
JDK > JRE > JVM。
JDK是最大的,它包含JRE,而JRE又包含JVM。
JDK是为了满足Java开发人员的需要而创建的,其中包含开发工具和JRE。因此,如果你需要编写Java程序,那么你需要JDK。
JRE是为了运行那些已经编写好的Java程序而创建的,JRE中包含有JVM和Java类库,但是并不包含其他开发工具,因此,如果你只需要运行Java程序,那么你只需要JRE就可以了。
JVM就像一个桥梁,它负责将我们编写的Java代码(人类可以理解的)转化为机器可以运行的机器代码。
JDK、JRE和JVM的区别
对于初学者来说,JDK、JRE和JVM这三者之间的区别可能会让人感到困惑。简单来说:
JDK是开发工具,它让程序员编写Java程序。JRE是运行环境,它让编写好的Java程序可以被运行。JVM则是JRE的一部分,位于程序执行的最前沿,将字节码转化为机器代码。 Java技术的一大优势就在于它的平台无关性,开发者可以编写一次代码,然后在任何运行着JVM的机器上运行这段代码。这个特性离不开JDK、JRE和JVM的作用以及三者之间的关系。理解这三者,就等于理解了Java平台的基础运行机制。
总的来说,对JDK、JRE和JVM深入了解是每一个Java开发者的基本功。只有理解了这些基础知识,我们才能更好地理解Java是如何运行的,并更好地进行编程工作。
在Node.js中,可以使用内置的fs模块来读取和写入JSON对象到文件。以下是具体的步骤和示例代码:
写入JSON对象到文件 使用fs.writeFile或fs.writeFileSync方法来写入文件。使用JSON.stringify方法将JavaScript对象转换为JSON格式的字符串。 异步方式:
const fs = require('fs'); const obj = { name: '张三', age: 30, city: '北京' }; // 将对象转换为JSON字符串 const data = JSON.stringify(obj); // 异步写入文件 fs.writeFile('data.json', data, (err) => { if (err) throw err; console.log('数据已写入文件'); }); 同步方式:
const fs = require('fs'); const obj = { name: '李四', age: 25, city: '上海' }; // 将对象转换为JSON字符串 const data = JSON.stringify(obj); // 同步写入文件 fs.writeFileSync('data.json', data); console.log('数据已写入文件'); 读取JSON对象从文件 使用fs.readFile或fs.readFileSync方法来读取文件。使用JSON.parse方法将JSON字符串转换回JavaScript对象。 异步方式:
const fs = require('fs'); // 异步读取文件 fs.
如何展示复选框 //LVS_EX_CHECKBOXES每一行的最前面带个复选框 //LVS_EX_FULLROWSELECT整行选中 //LVS_EX_GRIDLINES网格线 //LVS_EX_HEADERDRAGDROP列表头可以拖动 m_listctl.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES | LVS_EX_GRIDLINES); 全选,全不选,反选实现 // 全选 for (int i = 0; i < m_listctl.GetItemCount(); i++) { m_listctl.SetCheck(i, TRUE); m_listctl.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED); } // 全不选 for (int i = 0; i < m_listctl.GetItemCount(); i++) { m_listctl.SetCheck(i, FALSE); m_listctl.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED); } //反选 BOOL state; for (int i = 0; i < m_listctl.GetItemCount(); i++) { state = m_listctl.GetCheck(i); if (state == FALSE) { m_listctl.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED); m_listctl.
😊 @ 作者: 一恍过去 💖 @ 主页: https://blog.csdn.net/zhuocailing3390 🎊 @ 社区: Java技术栈交流 🎉 @ 主题: SpringBoot+ENC实现密钥加密及使用原理 ⏱️ @ 创作时间: 2024年06月23日 目录 前言1、整合SpringBoot1.1、POM1.2、加密盐值配置1.3、工具类使用1.4、加密配置使用1.5、测试 2、ENC加载原理 前言 Spring Boot中使用ENC(Environment-Neutral Configuration)主要是为了将配置信息从应用程序代码中分离出来,以提高安全性和可维护性。ENC的主要优点包括:
安全性增强: 敏感信息(如数据库密码、API密钥等)不应硬编码在代码中,而是应该使用加密的方式存储在配置文件中,然后通过ENC进行解密和使用,从而减少泄露风险。可维护性: 将配置信息与代码分离,使得配置可以独立地修改和管理,而不需要重新编译和部署应用程序。这样可以降低维护成本,并使应用程序更易于管理。灵活性: 使用ENC可以根据不同的环境(开发、测试、生产等)提供不同的配置,而不需要修改应用程序代码,从而提高了部署的灵活性和可移植性。 1、整合SpringBoot 1.1、POM <dependencies> <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>3.0.5</version> </dependency> </dependencies> 1.2、加密盐值配置 整合SpringBoot时,盐值的配置最好式写到配置中心的文件中,盐值写到本地文件,泄露后也容易被进行解密
jasypt: encryptor: # password值任意,最好随机字符 password: hhX4FzbwcT 1.3、工具类使用 使用工具类对需要处理的明文数据进行加密处理,再将加密结果写入到配置文件中
注意:工具类使用完成后,应该删除加密盐
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; public class JasyptTest { /** * 加密盐值,使用完成后进行删除,或者不能提交到`生产环境`,比如: */ private final static String PASSWORD = "hhX4FzbwcT"; public static void main(String[] args) { PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); SimpleStringPBEConfig config = new SimpleStringPBEConfig(); // 用于设置加密密钥。密钥是用于加密和解密字符串的关键信息。 config.
准备 安装docker 自行下载虚拟机,安装Centos7系统,并能够在虚拟机中启动成功下载Xshell,Xftxp 安装过程
在Linux中下载yum-utils工具,
然后指定下载源,让yum去这个位置下载docker sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo,下载docker设置docker的插件: sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
验证: docker -v, docker images,查看自己的docker是否安装成功配置镜像加速: 到阿里云,镜像中设置自己的加速配置 命令在阿里云->容器->容器镜像->管理控制台启动docker: systemctl start docker 安装容器 拉取镜像文件 docker pull+镜像文件名字,镜像可以去docker-hub官网找
创建容器 docker run -d --name mysql -p 3306:3306 -e TZ=Asia/Shanghai -e MYSQL_ROOT_PASSWORD = 123 mysql
-d:表示容器后台运行
-p:指明了容器的端口和寄存器的端口相映射
-e:指明了该容器的环境配置
--name:为该容器命名
docker run :固定语法,指在创建一个容器
最后的mysql指的是拉取的镜像文件的名字,如有需要,在后面写上该镜像文件的版本,name:targ
docker的常见命令:
docker start 容器名,启动该容器
docker stop 容器名,关闭该容器
docker status 容器名,查看容器状态
docker ps 查看所有容器的信息
Canvas简介 Canvas是HTML5中的标签,用以生成图像。这里的生成图像有点类似于我们自己在纸面上绘画,需要指定渲染位置以及大小等参数,同时,这也使得一旦元素被绘制出来,便再也无法编辑,只能擦除后重新绘制。
Canvas支持的浏览器 除IE8及更早版本外,其余浏览器如IE9、Edge、Chrome、FireFox、 Safari等支持Canvas。
预期结果 其中,(1)为游戏新加载时显示的图像,有两个大小为80px*100px的矩形,一个半径为15px的红色小球;(2)当鼠标左键被按下时,木棍增长,最高不超过Canvas页面外;(3)松开鼠标左键,木棍倒下,如果木棍顶端正好位于另外一个矩形顶部范围内,游戏继续,清除页面内容,开始生成下一个矩形;(4)如果木棍顶端没有到达或超出矩形顶部范围,游戏结束,弹出“重新开始”按钮。
项目结构 包含HTML主页面stickGrow.html、css样式文件stick.css和JavaScript文件stick.js。 HTML主页面:stickGrow.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Horizon Project</title> <link rel="stylesheet" href="./stick.css"> </head> <body> <div class="con"> <div class="score">0</div> <canvas width="400px" height="600px" id="cvs"></canvas> <button class="restart">重新开始</button> </div> </body> </html> <script src="./stick.js"></script> Canvas使用<Canvas>标签创建,并直接向其赋值:宽width和高height。创建用来显示得分的div--”score“,其内部赋初始值0。最后是“重新开始”按钮。运行后得到如下结果:
按照正常使用习惯,我们接下来要将得分和按钮移动进Canvas内,且暂时隐藏“重新开始”按钮,因此需要使用css来完成。
css样式文件:stick.css body{ margin: 0; padding: 0; background-color: black; } .con{ width: 400px; height: 660px; margin-top: 50px; margin-left: auto; margin-right: auto; } .score{ width: 180px; height: 60px; text-align: center; margin-left: auto; margin-right: auto; color: #eb4b16; font-size: 60px; position: relative; top: 120px; text-shadow: 5px 5px 10px #b18253; } 首先给Canvas的父级div--“con”赋宽高值。此处使用position: relative;移动了分数的位置,但是HTML还是认为score在Canvas上方占据60px。因此con的高 = Canvas的高 + score的高,为660px。同时将margin-left和margin-right设为auto使其始终位于页面中央。
目录 一,快速排序1. 挖坑法2. 快速排序的优化3. Hoare法4. 前后"指针"法 二,快速排序总结:三,冒泡排序 一,快速排序 快速排序是一种比较复杂的排序算法,它总共有4种实现方式,分别是挖坑法,左右"指针"法,前后"指针"法,以及非递归的快速排序 (本文只讲述递归实现,非递归实现以后有专门的文章) ,并且这些算法中也会涉及多种优化措施,比如三数取中,小区间优化,下面都会一一介绍。
由于它效率极高的缘故,快速排序也是日常开发中使用最多的,最重要的排序算法。
1. 挖坑法 1.1 基本思想:
任取待排序元素序列中的某元素(一般选最左边或最右边的元素)作为基准值(也叫做 key 关键字),按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
1.2 一趟排序图解如下:
给定一无序数组,选第一个元素为关键字 key = 6
我们选定关键字 key = 6后,就说明6的位置就可以被覆盖了,所以我们就说左边形成了一个****坑,用pivot 表示。
左边有坑,右边的 end 要从最后一个元素开始找比 key 小的数,找到后放到左边的坑里,所以5放进了坑中
5被拿走之后,右边它原来所在的位置就形成了一个新坑,此时,左边的 begin 要开始找比 key 大的数,找到后放到右边的坑里,所以7放进了坑中
7被拿走后,左边又形成了一个新坑,此时,end 又要开始找比 key 小的数放到左边的坑里,所以4放进了坑中
此时,右边又形成了新坑,begin 要开始找比 key 大的数,找到后放到右边的坑里,所以9放进了坑中
左边又形成了坑,右边 end 开始找,找到了3,放入坑中
最后一次 begin++ 后,begin 和 end 重叠了,并且它们一定相遇在坑中,此时,把 key 放入坑中即可。
上述操作只是第一趟排序,只排好了一个数,此时第一个基准 key = 6已经在它合适的位置上了(排好序后的位置),后面对左右子序列排序时6不动。并且已经把数组分成了两个子序列,以 key 为基准,左边的元素都比它小,右边的元素都比它大。
1.3 单趟排序的代码实现如下:
注意:第二个和第三个 while 中的 begin < end 不能缺少,要防止在找大和找小的时候 begin 和 end 错开或是在极端情况下(比如已经升序时)end一直减导致越界。
目录 1. 堆排序1.1排序思想1.2 代码实现 2. 选择排序2.1 排序思想:2.2 代码实现 3. 堆排序和选择排序的性能比较 1. 堆排序 堆排序是一种比较复杂的排序算法,因为它的流程比较多,理解起来不会像冒泡排序和选择排序那样直观。
1.1 堆的结构
要理解堆排序,首先要理解堆。堆的逻辑结构是一棵完全二叉树,物理结构是一个数组。 (如果不知道什么是二叉树,请前往我的主页查看)。所以堆是一个用数组表示的完全二叉树。如图:
1.2 堆的左右子树与下标的关系
现在的需求是要对数组元素进行排序,所以事实上我们还是通过数组的下标来操纵数组的元素。但是我们已经把数组想象成一棵完全二叉树了,怎么通过二叉树的左右子树来确定数组下标呢?有如下性质:
leftchild = parent * 2 + 1rightchild = parent * 2 + 2parent = (child - 1) /2 (child 是左孩子或右孩子)堆的右子树 = 左子树 + 1 1.3 大堆和小堆的概念
大(顶)堆:是指所有父亲节点的值都大于等于孩子节点的值。大堆的堆顶是数组元素的最大值。小(顶)堆:是指所有父亲节点的值都小于等于孩子节点的值。小堆的堆顶是数组元素的最小值。 堆排序主要分三步:
(1)构建堆
(2)调整堆
(3)堆排序
首先需要明确一点,构建堆是在数组基础上构建的,换句话说就是将数组抽象成一个二叉堆,而不是凭空构建。
1.1排序思想 1.首先将待排序的数组构造一个大根堆,此时,整个数组的最大值就是堆结构的顶端。
2.将堆结构内顶端的数与堆的最后一个叶节点所在的数交换,此时,末尾的数为最大值,把它不看作堆里面的了,剩余待排序的个数为n - 1。
3.将剩余的n - 1个数再构造成大根堆,再将堆顶的数与n - 1位置的数交换,如此反复执行,最后就能得到有序数组了。
注意:排升序建大根堆,排降序建小根堆。(默认排升序)
原因:由于堆排序的本质是选数排序,是通过堆来选数的。如果排升序时建小堆,最小的数在堆顶已经被选出来了。那么在剩下的数中再去选数,但是这时剩下的数的父子结构关系都乱了,需要重新建堆才能选出下一个数,建堆的时间复杂度是0(N),这样堆排序就没有效率优势了。
如何构造大堆 想要建大堆,首先要理解向下调整算法,前提是左右子树都是大堆,否则无法使用该算法(如果要建小堆,则使用向下调整算法的前提是左右子树都是小堆)。
算法思路:
从根节点开始,选出左右孩子中大的那一个,跟父亲比较,如果比父亲大就和父亲交换位置,然后再继续向下调,调到叶节点就终止。
举一个简单的例子解释:
目录 1. 递归实现的缺陷2. 递归改非递归的实现方式3. 快速排序的非递归 --- 使用栈4. 归并排序的非递归 --- 使用循环 1. 递归实现的缺陷 在以前的文章中我们把快速排序和归并排序的递归实现方式进行了介绍,但是在校招面试和在企业的日常开发过程中,仅掌握递归方法是不够的,因为递归也有它的缺陷。
我们知道在函数调用过程中会在内存中建立栈帧,栈帧的建立是会消耗空间的。而递归最致命的缺陷就是:在极端情况下,当栈帧的深度太深时,栈空间不够用,就会导致栈溢出!
1.1 栈溢出的例子
可以举一个简单的例子来证明存在栈溢出的情况。
比如:我们用递归实现 1 + 2 + 3 + …… + n 的求和。
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> int func(int n) { return n == 1 ? 1 : n + func(n - 1); } int main() { int n = 0; scanf("%d", &n); int sum = func(n); printf("%d\n", sum); return 0; } 当输入的 n = 10000 时 ,调试结果如下:
🤖AI学会篡改奖励函数、欺骗研究者!Claude团队:无法根除的行为,令人不安
- 实验中让AI可以访问自己的强化学习代码,并且提问:目前为止,我们总共进行了几轮强化学习?AI在自以为不会被看见的草稿纸中写下内心OS
- 研究对未来如何避免强大的AI系统出现这种问题非常有意义。
- Anthropic、Readwood Research(专注AI安全的非盈利研究机构)和牛津大学合作研究:从投机取巧训练中学会不正当行为。
🔗 AI学会篡改奖励函数、欺骗研究者!Claude团队:无法根除的行为,令人不安-CSDN博客
🌐LibreChat:一个免费的开源 ChatGPT 克隆版 - 可以接入各种 AI 模型
- 它支持与OpenAI、Azure、Anthropic和Google等AI模型服务的集成。
- 可以在对话过程中自由切换AI模型,还可以使用DALL-E或Stable Diffusion等插件进行图像生成。
- 通过OpenAI助理 API 支持高级代理、代码解释器、工具和API操作。
🔗官网: https://www.librechat.ai/l
🔗GitHub: https://github.com/danny-avila/LibreChat
🔗在线体验: https://librechat-librechat.hf.space/
🔗 LibreChat:一个免费的开源ChatGPT克隆版可以接-CSDN blink-领先的开发者技术社区
🔊RTranslator :一款开源、免费离线的实时翻译应用 - 可实现多人、多语言的实时对话翻译
- 用户可以通过蓝牙耳机连接应用,将手机放进口袋,与他人进行实时语言转换的对话,应用会自动翻译并播报对方的语言。
- 包括对话模式、对讲机模式和文本翻译三种模式。其中对话模式通过对方的手机或蓝牙耳机播放。
- 可以连接多个设备,实现多人、多语言的实时对话翻译。
- 对讲机模式通过麦克风播放翻译语音,交替进行对话,不支持蓝牙耳机。
🔗下载:https://github.com/niedev/RTranslator 🔗 RTranslator:一款开源、免费离线的实时翻译应用可实-CSDN blink-领先的开发者技术社区 📱华为发布会杀疯了:盘古大模型跳级发布,编程语言仓颉首次亮相...
- 华为现场演示搭载盘古大模型的人形机器人:能完成10步以上的复杂任务规划。 - 盘古大模型强调两大能力:多模态和强思维。
- HarmonyOS NEXT现在已正式开启面向开发者和先锋用户的beta升级。
- 华为自研仓颉编程语言正式亮相,鸿蒙生态补齐“最后一环”。
🔗 华为发布会杀疯了:盘古大模型跳级发布,编程语言仓颉首次亮相...-CSDN博客
前言👀~ 数据库的知识点先暂且分享到这,接下来开始接触计算机组成以及计算机网络相关的知识点,这一章先介绍一些基础的计算机组成知识
一台计算机如何组成的?
存储器
CPU
cpu的工作流程
主频
如何衡量CPU好坏呢?
指令
操作系统
操作系统功能
操作系统"内核" 如果各位对文章的内容感兴趣的话,请点点小赞,关注一手不迷路,如果内容有什么问题的话,欢迎各位评论纠正 🤞🤞🤞
个人主页:N_0050-CSDN博客
相关专栏:java SE_N_0050的博客-CSDN博客 java数据结构_N_0050的博客-CSDN博客
一台计算机如何组成的? cpu(控制器和运算器等)、存储器(主存和辅存)、输入设备、输出设备
计算机工作流程:这里我简单举个例子去描述,你用鼠标点开了一个游戏(指令和数据),存储到存储器当中(从外存中加载到内存中),然后cpu从存储器中取出指令对其解码后进行运算,再传回存储器然后由输出设备进行输出显示到你的电脑上
存储器 提到存储器就可以想到内存和外存:
内存:通常指的是随机存取存储器,一种主存储器,读写速度快,存储容量小,主要存储计算机正在执行程序和数据(程序执行的时候需要先放到内存当中才能被cpu处理),当你打开一个程序,操作系统会将这个程序从外存中被加载到内存当中,以便cpu快速访问和执行。另外内存属于易失性存储器,断电的时候数据会丢失。
内存的作用:
存储临时数据:内存用于存放计算机在运行过程中需要快速访问和修改的临时数据。例如,当你运行一个程序时,这个程序的代码和数据会被加载到内存中,以便CPU快速访问和执行。
提高程序运行效率:操作系统和应用程序执行的时候,其指令和数据会存储到内存当中,因为内存具有快速读写的能力
缓存功能:内存可以作为缓存(主存和辅存之间),以减少访问外存的频率也就是减少访问硬盘的频率这样会节省不少的开销,当数据第一次从外存中加载到内存当中之后,下次再访问相同的数据的时候,我们直接从内存中进行读取即可,这样能提升我们的访问速度。就比如我们打开一个游戏第一次从外存中加载到内存中(第一次打开这个游戏的过程是不是很慢),我们只要没有关掉它,你再次点击它又回到游戏(这里游戏已经加载到内存中了,你不需要再从外存中读取,再次点击的时候立马回到游戏)
内存的工作原理:
1.加载程序:当我们运行一个程序时,操作系统会将这个程序从外存中被加载到内存当中,这个程序包括可执行文件、相关的数据文件等等
2.CPU访问:cpu通过地址总线快速访问内存中的指令和数据,内存通过数据总线和地址总线和cpu进行通信,快速提供所需数据
3.执行指令:cpu从内存中读取到指令和数据然后去执行这些指令
4.数据交换:cpu处理了这些指令把结果传回内存,内存中的数据发生变化,会临时存储在内存当中直到程序结束明确保存到外存中。
外存:是一种辅助存储器,读写速度慢,存储容量大,主要用于长期存储我们的文件和数据,例如我们的硬盘就是外存,我们会把文件存储在当中。程序运行的时候我们把外存的数据复制到内存,可以把内存看作是外存的高速缓存。硬盘属于非易失性存储器,即使断电我们的文件和数据依然保存在硬盘当中,确保数据的持久性。
CPU CPU中央处理器,就是那块小小的硬件,属于计算机的核心也可以理解为是计算机的大脑,负责处理各种指令,它是由寄存器、运算器(ALU)、控制器组成
加快CPU访问速度:在cpu内部有个高速缓存存储器,也称Cache读写速度比内存还快的存储器,当cpu向内存写入数据,Cache中也会存储,当cpu需要读取这些数据时,这些数据会从高速缓存中读取这些数据(可以这么理解Cache 中的内容是主存中部分内容的副本)
Cache又分一级缓存、二级缓存、三级缓存,一级缓存离cpu最近,一级缓存又可以分一级数据缓存和一级指令缓存,一个用于处理数据一个用于对这些数据的指令进行解码,并且两者都能被cpu同时访问,提到了cpu的效率
寄存器:通用寄存器主要负责存储临时的数据和指令,记住cpu拿数据不是直接从内存中拿的,是通过寄存器中获取的
运算器:也称算术逻辑单元(ALU),算术逻辑单元呢又是由算术单元和逻辑单元组成的。算术逻辑单元主要就是负责计算的一个部件
控制器:也称控制单元(CU),控制单元由指令寄存器(IR)、指令译码器(ID)、程序计数器(PC)、操作控制器(OC)、时序产生器组成,可以把它理解为你的上司,它指东你就得往东走,你不能往西走。是cpu的指挥中心,指挥cpu中的各部件按照指令进行工作的部件
指令寄存器:存储当前要执行的指令
指令译码器:对指令进行解码(翻译)
程序计数器(寄存器):存储下一条要执行的指令的地址
cpu的工作流程 1.取指令:首先从程序计数器中得到指令的地址,接着去内存中取指令,然后存储到指令寄存器当中。
2.译指令:从指令寄存器当中取出指令,指令译码器对其进行解码,确定是什么类型的指令以及需要哪些操作数
3.执行指令:如果需要操作数的话cpu会从内存或寄存器当中取操作数(这里可以再细分一个阶段),接着运算器执行解码后指令的操作
4.写回:处理后的结果存储在cpu内部寄存器中,有些情况下会将执行后的结果写回到内存当中。写回阶段的作用就是将数据存储到正确的位置,以便后续指令使用
5.更新pc:当一条指令被取出后,程序计数器会增加(表示下一条要执行的指令的地址)
cpu中断机制:中断顾名思义暂时停止当前的活动,cpu中断分为硬件中断(硬件设备鼠标、键盘点击触发的)和软件中断(软件指令触发的,如系统调用)。举个例子比如我们在玩游戏的时候,女朋友发消息过来你直接切到微信回消息后又继续游戏了,在切微信再切回游戏这个过程简单点说向cpu发起中断请求然后通过上下文完成中断,举个例子你在吃饭然后突然女朋友打电话叫你去接她,你先把这个吃饭的地方的地址记住然后去接她过来然后接着吃饭
上下文信息:保存寄存器、程序计数器等当中的信息
主频 图中的2.90GHz是CPU的主频,可以理解为一秒钟cpu可以执行29亿条指令(不严谨),方便理解,图中的只是个下限,任务管理器可以看到实时主频,主频会根据当前的任务的负载程度,不断变化
当前频率,睿频,也有上限,根据CPU决定越好上限越高
如何衡量CPU好坏呢? 最关注的指标有两方面,一个是主频、一个是核心数
最开始CPU都是单个核心(核心可以理解为一个能完成完整计算功能的整体,是由很多的计算单元构成的),然后通过提高集成程度,提高cpu的速度。不断的减小计算单元的体积,然后提高集成程度从而提高cpu的速度,但是体积小到一定程度不能再小了这是就限制住了,现在都是多核心,去提高cpu的速度
举个例子可以把起初的cpu理解成一个小工厂,只有一个车间(单核),车间里的员工负责生产工作(运算器),车间里比员工还牛马的负责搬运工作或者传送信息(寄存器),车间里喊你干活叫你别偷懒指挥你的(控制器)。随着车的销量上去了,但是效率没上去,于是老板就多搞了好多个车间(多核),这样效率就上去了。但是又出现一个问题材料的不断增多(高并发),牛马搬不过来了。于是工厂搞来了手推车(Cache)减轻了这个问题
指令 cpu上能够执行的任务的最小单元(最小单元由 二进制 的方式表示的机器语言)
指令=操作码+地址码
指令周期是指执行一条指令所需的时间,它通常由若干个机器周期组成。
一个cpu设计的时候,会提供一些可以进行的操作就是支持哪些指令,比如加法指令、读取内存指令、写入内存指令等,这些是cpu能够执行任务的最小单元,执行其他任务的时候都是由这些最小单元构造的
怎么说呢?你给cpu发送一个任务,例如让它帮你去接杯水,得先去拿杯子,去哪里拿呢?要一步一步说清楚说的很细很细,然后去哪里接?也要一步一步说清楚说的很细很细。可以把这一步一步很细致的过程理解为一系列最小单元的指令,通过执行这些最小单元指令,CPU才能逐步完成整个任务
操作系统 操作系统是管理计算机硬件与软件资源的程序/软件
🌈 个人主页:danci_
🔥 系列专栏:《设计模式》《MYSQL》
💪🏻 制定明确可量化的目标,坚持默默的做事。
✨欢迎加入探索MYSQL索引数据结构之旅✨
👋 大家好!文本学习研究事务隔离级别。👋 无论您是刚接触MySQL的初学者,还是希望深入优化性能的资深开发者,这篇文章都将为您揭开MySQL事务隔离级别的神秘面纱,让您掌握其中的奥秘,进而提升数据库操作的效率和精度。快来一起探索吧!
1. 什么是事务?
目录
一、事务隔离级别
1.1 事务并发执行的一致性问题
1.1.1 脏写
1.1.2 脏读
1.1.3 不可重复读:
1.1.4 幻读
1.2 SQL标准中的4种隔离级别
1.3 MYSQL查询事务隔离级别
1.4 MYSQL设置隔离级别
二、MYSQL4种隔离级别实战
2.1 READ UNCOMMITTED
2.2 READ COMMITTED
2.3 REPEATABLE READ(MYSQL默认的隔离级别)
2.4 SERIALIZABLE
三、总结
一、事务隔离级别 MYSQL是客户端 / 服务器的软件,是多对一的关系,即同时可以有多个客户端同时连一个接服务,每个客户端连接服务器后,就生成一个会话。每个会话都可以向服务器发送请求语句,一个请求语句可能是一个事务,也可能是一个事务的某一部分语句。而服务器可以同时处理来自多个客户端的请求语句。
事务简述:一个事务就对应着现实世界的一次状态转换。 下面举个例子,比如用户A向用户B转账100元,账户状态变更简化为以下几个步骤:
取出A账户余额a1。即一次select A a1 < 100,退出转账a1 >= 100,则继续a1 -= 100更新入库。即一次update A取出B账户余额b1。即一次select Bb1 += 100更新入库。即一次update B 在这个转账事务中,一定要保证 A减100 和 B加100 都成功,换句话说就是必须保证参与转账的账户的总余额保持不变,这也就是这个转账事务的一致性要求。
如果事务是以单个的形式一个接一个地执行,那么在一个事务开始时,面对的就是上一个事务执行结束后留下的一致性状态,它执行之后又会产生下一个一致性状态。那么在多个事务的情况下,情况就变得比较复杂。假如事务是交替执行的,如下图
随着近些年lua语言在安卓游戏大火,lua逆向已经越来越重要。早期游戏的lua源码放在安装包的assets目录,但随着对抗的升级,越来越多的游戏使用动态更新和扩展应用功能的方式,特别是在需要频繁更新脚本或配置的情况下。这种方法在游戏开发、插件系统、远程配置等场景中有较多应用。而且这种方法是网络上加载Lua代码,你在安装包,APK是得不到lua的代码的。遇到这种我们首先要确认是否是lua语言的应用:
打开apk\lib\arm64-v8a\目录,如果发现libxlua.so,libslua.so等等类似目录,那么有很大可能是lua。(lua又分luajit与普通lua,判断是不是luajit,将so文件放进IDA,字符串搜索luajit即可)
再使用IDA或者frida hook lual_loadbuffer获取字节码,使用unluac网址(unluac download | SourceForge.net)进行反编译。
java -jar unluac.jar 你的字节码文件>存放文件
反编译成功一般不会有输出信息,但看到类似上面的情况大概有3种可能:
1.文件头被改
2.opcode修改
3.文件头和opcode都被改
关于文件头:
0到3字节:1B 4C 75 61表示这是一个Lua字节码文件。
第4字节:51:版本号,表示Lua 5.1。
第5字节:00:格式号,表示标准格式。01为非官方,魔改lua。
第6字节:01:endianness标识,表示大端格式。字节码文件需要指示其数据的字节顺序,以确保在不同平台和硬件架构上的正确解析。不同平台可能采用不同的字节序,比如大多数x86和x86-64架构采用小端序,而一些嵌入式系统和网络协议采用大端序。
第8字节到11字节:Lua整数大小,指令大小等。
关于opcode修改:liblua.so文件是lua源码文件编译的,lua源码的"lua-5.1.5\src\lopcodes.h"文件定义了opcode:
如果在编译的时候将opcode的顺序修改,比如OP_MOVE在OP_GETUPVAL前面,改为OP_GETUPVAL在OP_MOVE前面,再编译为so,这时再使用unluac反编译会报错。
解决办法有两个:
一:将正常的so文件与修改后的so文件,放进IDA反编译,搜索函数:luaV_execute找到case,进行对比,还原opcode的顺序。 还原opcode之后,修改unluac的源码的src/unluac/decompile/OpcodeMap.java文件:
再使用unluac反编译。
二:[原创]用 Lua 简单还原 OpCode 顺序-Android安全-看雪-安全社区|安全招聘|kanxue.com
大佬的办法,从应用内部运行lua脚本,获取字节码。再将同一个lua脚本运行在未被修改的环境。
使用python将两个字节码文件对比,将红色的部分使用python替换,然后使用unluac反编译.
反编译成功!
allWebPlugin简介 allWebPlugin中间件是一款为用户提供安全、可靠、便捷的浏览器插件服务的中间件产品,致力于将浏览器插件重新应用到所有浏览器。它将现有ActiveX插件直接嵌入浏览器,实现插件加载、界面显示、接口调用、事件回调等。支持谷歌、火狐等浏览器,接口调用友好、集成方便。为用户提供“信息化系统 + allWebPlugin + 插件 + 浏览器”的解决方案。 浏览器插件淡出过程 2015年4月14日,谷歌浏览器从42版本开始不支持NPAPI。 2017年3月7日,Firefox 从v52 版本停止支持所有的 NPAPI 插件。
2022年6月15日,IE浏览器桌面程序正式退役,ActiveX插件无用武之地。
2022年10月25日,谷歌发布Chrome107版浏览器,终止支持所有平台上PPAPI插件接口。
至此,市面上新版浏览器都不再支持浏览器插件技术。
iWebOffice2015 插件现状 金格科技iWebOffice2015智能文档中间件,是一款在OA办公系统使用广泛的浏览器插件产品,帮助用户解决流式文档应用的场景。由于新chrome浏览器不在支持插件技术,导致新版浏览器无法使用。如下图所示:
iWebOffice2015 高版本Edge浏览器效果 在allWebPlugin中间件助力下的效果 在allWebPlugin中间件演示实例中,包含金格iWebOffice2015 智能文档插件在allWebPlugin中间件技术的帮助下,成功在浏览器展示及应用。
allWebPlugin中间件自带iWebOffice2015插件演示实例 新建文档功能 打开本地文档 VBA套红 演示版下载地址 链接:https://pan.baidu.com/s/1xUyQDzOabh7mU7J7TYhtig?pwd=z3q0 提取码:z3q0 在Chrome、火狐等浏览器中输入http://127.0.0.1:6651,选择其中任何一个插件即可查看演示效果。
数据结构之红黑树 前言 红黑树是一个相对比较复杂的数据结构,单从文字来讲解,还是会有很难理解的地方对此,我很早之前就在B站中就出了讲解视频,大家可以通过这篇文章以及视频辅助理解,B站个人地址 红黑树(RB-tree) 概念:
红黑树是AVL树的变种,它是每一个节点或者着成红色,或者着成黑色的一棵二叉查找树。对红黑树的操作在最坏情形下花费O(logN)时间,它的插入操作使用的是非递归形式实现红黑树的高度最多是2log(N+1) 特性:
红黑树是具有着色性质的二叉查找树,也就意味着树的节点值是有序的,且每个节点只可能是红色或者黑色红黑树的根是黑色的如果一个节点是红色的,那么它的子节点必须是黑色的从一个节点到一个空指针的每一条路径必须包含相同数目的黑色节点 自顶向下插入操作:
如果使用自底向上插入的话还需要进行逐步递归是他们保证满足红黑树特性,效率就降低了。
令X为新插入节点(在下面的第三操作中为当前节点),P为X的父节点,G为P的父节点(也就是X的祖父节点),GP为G的父节点(也就是P的祖父节点,X的曾祖父节点)
因为红黑树是一颗二叉查找树,因此在插入时需要查找要插入的值的正确位置,在这个查找路径中,如果遇到节点(X)为黑色而子节点全部为红色,我们就进行翻转操作,也就是将该节点(X)着成红色,子节点全部着成黑色。翻转后:
如果翻转后发现P和X节点都是红色就需要根据树的结构进行旋转操作
如果X,P,G形成"一字形",则对P的父节点G(也就是X的祖父节点)与P进行单旋转,并将新根也就是P着成黑色,新根的子节点都着成红色。如果X,P,G形成"之字形",则对G与X节点进行双旋转,并将新根着成黑色(也就是X节点),然后将新根的子节点着成红色 如果该节点(X)是黑色则继续将X下降,直到找到红色节点继续翻转,或者找到指定插入位置,找到指定位置也就是当前节点位置X就进行插入,新节点也是红色,需要重新判断其父节点是否为红色,为红色又需要进行翻转操作来调整。
自顶向下删除操作
自顶向下删除也需要保证红黑树的性质,插入是插入一片红色的叶子节点,那么反过来我们删除一个红色叶子节点就不会破坏红黑树性质,自顶向下插入的翻转操作是将红色节点减少,并将红色节点上浮,因为删除是插入的逆过程,因此删除的翻转操作就是要将树中的红色节点增多,并将红色节点下沉,这样我们删除红色叶子节点的概率更大,并且不会破坏红黑树性质
删除操作一共有5种情况需要解决
要删除节点cur跟其兄弟节点s原本颜色为黑色,父亲节点p为红色s的两个儿子都是红色,这样双旋转和单旋转都可以,这里优先选择ps单选转调整,情况1-case4s的左儿子为红色,需要ps.l双旋转调整(s.l为s的左儿子),情况2-case1s的右儿子为红色,需要ps单旋转调整,情况3-case2s有两个黑色儿子,直接cur,p,s颜色翻转操作调整,情况4-case3p和cur为黑色,s为红色,需要交换sp节点的颜色,并且sp单旋转调整,情况5-case5cur为红色,可以继续将cur下降,也就是当前cur指向原本cur的子节点,如果为红色继续下降,如果为黑色就判断是否需要操作 tomove指向要删除节点也就是目标节点,而p指向真正要删除的叶子节点,cur则while循环完后则是指向nil节点,因为将tomove标记完,就进行cur和p就查找tomove右子树的最小值节点进行删除,而while循环终止条件为cur==nil情况,因此p指向真正要删除的节点
找到tomove和p后,将tomove的data等于p的data,将p删除,因为p为叶子节点,将p的父节点指向nil。
情况2-case1
情况3-case2
情况4-case3
情况1-case4
情况5-case5
计算红黑树层数:
需要对log2(树中总共节点数+1)向上取整 代码:
int Height(const int count){ return std::ceil(std::log2(count+1)); } 代码实现:
#include <iostream> #include <queue> #include <math.h> #include <limits.h> using namespace std; typedef enum {red,black} colortype; struct RBNode{ int data; RBNode *left,*right,*parent; colortype color; //颜色 RBNode(const int val,RBNode* l,RBNode* r,RBNode* p,colortype c=red):data(val), left(l),right(r),parent(p),color(c){}; }; class RBtree{ public: RBtree(){ nil=new RBNode(INT_MAX, nullptr, nullptr, nullptr,black); root= nullptr; t=new RBNode(INT_MIN,nil,nil,nil,black); size=0; } ~RBtree(){ clear(); delete t; delete nil; }; void insert(const int val); //插入操作 void del(const int val); //删除操作 RBNode* find(const int val); //查找操作 void print(); //打印操作,层序遍历 //清空操作 void clear(){ clear(root); root= nullptr; t->right=nil; size=0; } protected: void overturnred(const int val,RBNode* &cur); //翻转操作,将当前节点变成红色,子节点变成黑色 void overturnblack(int val,RBNode* &cur); //翻转操作,将当前节点变成黑色,子节点变成红色 RBNode* SingleRotatewithleft(RBNode* &k1); RBNode* SingleRotatewithright(RBNode* &k1); RBNode* Rotate(const int val,RBNode* &k1){ if(val<k1->data){ return k1->left=val<k1->left->data?
目录
1. 使用 <chrono>库(C++11及以后版本)
2. 使用<ctime>库(较旧但常用的方法)
3、使用第三方库(如Boost.Timer)
4. 使用Windows API函数(Windows平台特有)
1. 使用 <chrono> 库(C++11及以后版本) <chrono> 库提供了高精度的时间测量功能。
#include <iostream> #include <chrono> int main() { auto start = std::chrono::high_resolution_clock::now(); // Your code here // ... auto stop = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count(); std::cout << "Elapsed time: " << duration << " ms\n"; return 0; } 2. 使用 <ctime> 库(较旧但常用的方法) <ctime> 库提供了基于系统时间的函数clock()。
#include <iostream> #include <ctime> int main() { clock_t start = clock(); //也可以double start = clock(); // Your code here // .
偶然发现一个python字符串的现象:
>>> a = '123_abc'
>>> b = '123_abc'
>>> a is b
True
>>> c = 'abc#123'
>>> d = 'abc#123'
>>> c is d
False
这是为什么呢,原来它们的id不一样。
>>> id(a) == id(b)
True
>>> id(c) == id(d)
False
那为什么它们的地址有的相同,有的不同呢?查询后得知这是一种 Python 的字符串驻留机制。
字符串驻留机制 也称为字符串常量优化(string interning),是一种在 Python 解释器中自动进行的优化过程。它主要目的是减少内存的使用,提高程序的运行效率。
工作原理 小字符串:Python 只会对短小的字符串进行驻留。但是,这个长度并不是固定的,它可能会因 Python 的不同版本或实现而有所不同。字符串池(String Pool):Python 解释器维护一个字符串池,用于存储所有已经出现过的字符串常量。驻留(Interning):当解释器遇到一个新的字符串字面量时,它会首先检查这个字符串是否已经存在于字符串池中。如果存在,则直接使用池中的引用;如果不存在,就将这个字符串添加到池中,并返回这个字符串的引用。内存节省:由于相同的字符串字面量在程序中可能被多次使用,通过字符串驻留机制,可以确保这些重复的字符串只存储一次,从而节省内存。性能提升:字符串比较操作可以通过比较它们的引用地址来完成,这比逐字符比较要快得多。因此,字符串驻留可以提高字符串比较的性能。自动和透明:字符串驻留是自动进行的,程序员不需要显式地进行任何操作。Python 解释器会在后台处理这一过程。不可变类型:字符串驻留机制只适用于不可变类型,因为可变类型的对象内容可能会改变,这会使得引用地址比较失去意义。如果字符串可以修改,那么驻留机制可能会导致意外的副作用。限制:字符串驻留机制虽然有诸多好处,但也存在一些限制。例如,如果程序中使用了大量的动态生成的字符串,那么字符串驻留可能不会带来太大的好处,因为这些字符串可能不会被重复使用。字符串字面量:只有当字符串是字面量时,Python 才会尝试进行驻留。通过其他方式(如 str() 函数、字符串拼接等)创建的字符串通常不会被驻留。编译时驻留:字符串驻留是在 Python 源代码编译成字节码时进行的,而不是在运行时。这意味着在运行时动态生成的字符串通常不会被驻留。 显式驻留 Python 提供了一个sys库函数 intern(),允许程序员显式地将一个字符串驻留。使用这个函数可以手动控制字符串的驻留过程:
>>> from sys import intern
>>> s = intern('abc#123')
文章目录 什么是多源最短路问题?1.矩阵2.飞地的数量3.地图的最高点4.地图分析总结 什么是多源最短路问题? 多源最短路问题(Multi-Source Shortest Path Problem,MSSP)是图论中的一个经典问题,它的目标是在给定图中找到从多个源点到所有其他顶点的最短路径。这个问题可以视为单源最短路问题(Single-Source Shortest Path Problem, SSSP)的扩展。
什么是单源最短路问题呢?其实我们上次讲的就可以归结在单元最短路问题当中,其实单源最短路问题就是只有一个起点对应一个终点,求最短路径,而多源最短路问题则是多个起点,对应一个终点,求这多个起点到达终点的最短路径,那这种题我们该怎么做呢?
第一种做法就是将多源最短路问题转换为n个单源最短路问题,循环n次就解决了,但是这种做法是非常慢的。
第二种做法就是把多个节点看成一个整体进行一次单源最短路问题的解法。
这是单源最短路问题问题:
多源最短路问题:
我们可以将多源最短路问题的节点看成一个整体,这种方法不仅在计算机领域很常用,在物理数学也很常用,这种方法叫隔离法,我们可以忽略每个节点之间的差异省去了我们比较每个节点差异的过程。
1.矩阵 题目链接
题目:
样例输出和输入:
这道题大致的意思就是对一个矩阵做变化,这个矩阵中的数只有两种,一种是1一种是0,我们该如何变换呢?根据题意,变换后节点的值为当前节点的值离最近一个0的节点的距离。按照这个规律首先我们来看看下面的例子,首先零肯定是不会变的,因为零距离最近的零就是他本身,所以这里距离就是0,第二行的1距离最近的零很显然是上左右的零,距离都是1,第三行的1距离醉经的0是上面的0距离为1,但是第三行中间的零距离最近的零是2.
算法原理:
这里我们已经讲过了做这种题的模式,我们只需要先将所有的零全入到队列中,这些零看成一个整体,在入队列的过程中顺便可以把需要返回的distance数组初始化为-1,然后零的对应位置赋值为0,这里我们直接利用单元最短路向外广搜,也就是整体向外扩散。
这里红色部分表示我们第一次入进去的0,蓝色部分表示我们第一次扩散,第一次扩散出来的部分应该填1,然后接下来可以继续向外扩散,这里就不展示了。
代码展示:
class Solution { public: typedef pair<int, int> PII; int dx[4] = { 0,0,1,-1 }; int dy[4] = { 1,-1,0,0 }; vector<vector<int>> updateMatrix(vector<vector<int>>& mat) { int m = mat.size(); int n = mat[0].size(); vector<vector<int>> distance(m, vector<int>(n, -1)); queue<PII> q; for (int i = 0;i < m;i++) { for (int j = 0;j < n;j++) { if (mat[i][j] == 0) { q.