数据结构与算法面试专题——堆排序
完全二叉树
完全二叉树中如果每棵子树的最大值都在顶部就是大根堆
完全二叉树中如果每棵子树的最小值都在顶部就是小根堆
设计目标:完全二叉树的设计目标是高效地利用存储空间,同时便于进行层次遍历和数组存储。它的结构使得每个节点的子节点都可以通过简单的计算得到,从而实现快速的节点访问。
实现原理:完全二叉树是一棵满二叉树,除了最后一层外,每一层都被完全填充。最后一层的节点都集中在左边。这种结构可以用数组来存储,其中数组的索引对应节点的位置。例如,对于索引为i的节点,其左子节点的索引为2i+1,右子节点的索引为2i+2,父节点的索引为(i-1)/2。
优点:空间利用率高,易于实现数组存储。层次遍历简单,可以通过数组索引快速访问节点。
缺点:插入和删除节点可能比较耗时,特别是当需要保持完全二叉树的结构时,可能需要重新调整整个结构。
堆结构
堆结构就是用数组实现的完全二叉树结构
设计目标:堆结构的设计目标是实现高效的优先级队列和排序算法。它基于完全二叉树的结构,通过维护堆序性(父节点的值大于或等于子节点的值,或者小于或等于子节点的值)来实现快速的插入和删除操作。
实现原理:堆结构通常用数组来实现,其中数组的索引对应节点的位置。在堆中,每个父节点的值都满足一定的条件,例如最大堆中父节点的值大于或等于子节点的值,最小堆中父节点的值小于或等于子节点的值。插入操作通过在数组末尾添加元素,然后通过上浮(Percolate Up)来维护堆序性。删除操作通常是指删除堆顶元素,然后通过下沉(Percolate Down)来维护堆序性。
优点:插入和删除操作的时间复杂度为O(log n),在优先级队列和排序算法中性能优越。空间利用率高,用数组实现简单。
缺点:对于范围查询等操作不友好,无法高效地进行任意元素的查找和删除操作。
如何理解“堆”?
堆本质是一种特殊的树。关于这个树,只要满足两点要求,它就是一个堆:
- 堆是一个完全二叉树;
- 堆中每一个节点的值都必须大于等于(或小于等于)其子树中每个节点的值。
第一点,堆必须是一个完全二叉树。完全二叉树要求,除了最后一层,其他层的节点个数都是满的,最后一层的节点都靠左排列。
第二点,堆中的每个节点的值必须大于等于(或者小于等于)其子树中每个节点的值。实际上,我们还可以换一种说法,堆中每个节点的值都大于等于(或者小于等于)其左右子节点的值。这两种表述是等价的。
对于每个节点的值都大于等于子树中每个节点值的堆,我们叫作“大顶堆”。对于每个节点的值都小于等于子树中每个节点值的堆,我们叫作“小顶堆”。
堆结构的heapInsert
操作
将一个新元素插入到堆结构中,并维持堆的性质(即父节点的值大于或等于子节点的值,或者小于或等于子节点的值,视堆的类型而定)。
示例
假设有一个最大堆,堆数组为 [100, 85, 90, 80, 75, 70]
,现在需要插入元素 95
。
-
插入后数组变为
[100, 85, 90, 80, 75, 70, 95]
。 -
新元素的位置是 6(从 0 开始计数)。
-
比较新元素与父节点(位置
(6-1)/2=2
,值 90):95 > 90,交换两者位置,数组变为[100, 85, 95, 80, 75, 70, 90]
。 -
继续比较新元素与新的父节点(位置
(2-1)/2=0
,值 100):95 < 100,无需交换。 -
插入完成,堆性质得以维持。
优点:能够在 O(log n) 时间复杂度内完成插入操作。
缺点:需要调整堆的结构,可能导致多次数据交换。
堆结构的heapify
操作
在堆结构中,当某个节点的子节点可能违反堆的性质时,通过调整该节点及其子节点,维持堆的性质。
示例
假设有一个最大堆,堆数组为 [95, 85, 90, 80, 75, 70]
,现在需要对位置 0 的节点进行 heapify 操作。
-
该节点的值为 95,子节点为 85 和 90。
-
95 大于子节点,无需调整。
-
如果堆数组为
[75, 85, 90, 80, 75, 70]
,需要对位置 0 的节点进行 heapify。 -
比较子节点 85 和 90,选择较大的 90 作为候选。
-
交换 75 和 90,数组变为
[90, 85, 75, 80, 75, 70]
。 -
继续 heapify 位置 2 的节点(原来的 75)。
-
比较子节点 80 和 75,交换 75 和 80,数组变为
[90, 85, 80, 75, 75, 70]
。 -
继续 heapify 位置 3 的节点(原来的 75),没有子节点,调整完成。
优点:能够在 O(log n) 时间复杂度内完成调整操作。
缺点:需要遍历节点的子节点,可能导致多次数据交换。
堆结构的增大
当堆中的元素数量增加,需要扩展堆的存储空间。
-
通常使用动态数据结构,如动态数组或链表,来实现堆的动态增长。
-
当需要增大堆时,分配新的内存空间,并将原堆中的元素复制到新空间中。
示例
假设堆当前的存储数组大小为 10,已满。需要插入一个新元素,堆将自动增大到大小为 20。
堆结构的减少
当堆中的元素数量减少,可能需要缩小堆的存储空间,以节省内存。
-
释放堆中多余的存储空间,调整存储数组的大小。
-
可以通过动态数据结构的特性,如动态数组的收缩功能,实现堆的缩小。
示例
假设堆当前的存储数组大小为 20,但只有 5 个元素。可以将堆缩小到大小为 10,释放多余的空间。
优先级队列结构
设计目标
优先级队列的设计目标是允许高效地访问和操作优先级最高的元素。与普通队列中的先进先出(FIFO)原则不同,优先级队列中的元素根据其优先级进行排序,并且优先级最高的元素会被优先处理。
实现原理
优先级队列通常使用堆结构来实现,能够高效地维护元素的优先级顺序。
1. 插入操作(Enqueue)
将新元素添加到堆的末尾。
通过上浮(Percolate Up)操作,将新元素与父节点进行比较和交换,直到满足堆的性质。
2. 删除操作(Dequeue)
删除堆顶元素(优先级最高的元素)。
将堆的最后一个元素移动到堆顶。
通过下沉(Percolate Down)操作,将该元素与子节点进行比较和交换,直到满足堆的性质。
优点
插入和删除操作的时间复杂度为 O(log n),在需要动态维护优先级的场景中性能优越。
空间利用率高,可以用数组实现,节省存储空间。
缺点
不支持高效的随机访问和查询操作,无法快速找到任意元素。
对于某些操作(如批量插入),可能需要重新构建整个堆,导致较高的开销。
堆排序
堆排序是一种原地的、时间复杂度为O(n log n)的排序算法。
堆排序不是稳定的排序算法,因为在排序的过程,存在将堆的最后一个节点跟堆顶节点互换的操作,所以就有可能改变值相同数据的原始相对顺序。
实现步骤
-
先让整个数组都变成大根堆结构,建立堆的过程:
-
从上到下的方法,时间复杂度为O(N * logN)
-
从下到上的方法,时间复杂度为O(N)
-
-
把堆的最大值和堆末尾的值交换,然后减少堆的大小之后,再去调整堆,一直周而复始,时间复杂度为O(N * logN)
-
堆的大小减小成0之后,排序完成
实现代码
/*** 对给定的整数数组进行堆排序。* 堆排序是一种基于堆数据结构的排序算法,它的时间复杂度为O(N log N),空间复杂度为O(1)。** @param arr 待排序的整数数组*/public void heapSort(int[] arr) {// 如果数组为空或者长度小于2,直接返回,因为不需要排序if (arr == null || arr.length < 2) {return;}// O(N*logN)// 从数组的最后一个元素开始,依次将每个元素调整为大根堆for (int i = arr.length - 1; i >= 0; i--) {heapify(arr, i, arr.length);}// 初始化堆的大小为数组的长度int heapSize = arr.length;// 将堆顶元素(最大值)与堆的最后一个元素交换,并将堆的大小减1swap(arr, 0, --heapSize);// O(N*logN)// 当堆的大小大于0时,继续进行排序while (heapSize > 0) { // O(N)// 将堆顶元素调整为大根堆heapify(arr, 0, heapSize); // O(logN)// 将堆顶元素(最大值)与堆的最后一个元素交换,并将堆的大小减1swap(arr, 0, --heapSize); // O(1)}}/*** 将指定索引位置的元素插入到堆中,并调整堆结构,使其满足大根堆的性质。* 该方法会将新元素与其父节点比较,如果新元素大于父节点,则交换它们的位置,* 并继续向上比较,直到新元素小于等于父节点或到达堆顶。** @param arr 包含堆元素的数组* @param index 要插入的元素的索引*/public void heapInsert(int[] arr, int index) {// 当当前元素大于其父节点时,交换它们的位置,并更新当前元素的索引while (arr[index] > arr[(index - 1) / 2]) {swap(arr, index, (index - 1) / 2);index = (index - 1) / 2;}}/*** 调整指定索引位置的元素,使其满足大根堆的性质。* 该方法会将指定元素与其子节点比较,如果该元素小于其子节点中的最大值,* 则交换它们的位置,并继续向下比较,直到该元素大于等于其子节点或到达叶子节点。** @param arr 包含堆元素的数组* @param index 要调整的元素的索引* @param heapSize 当前堆的大小*/public void heapify(int[] arr, int index, int heapSize) {// 计算左孩子的下标int left = index * 2 + 1; // 当左孩子存在时,继续循环while (left < heapSize) { // 两个孩子中,谁的值大,把下标给largest// 1)只有左孩子,left -> largest// 2) 同时有左孩子和右孩子,右孩子的值<= 左孩子的值,left -> largest// 3) 同时有左孩子和右孩子并且右孩子的值> 左孩子的值, right -> largest// 找出左右孩子中值较大的那个的下标int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;// 比较父节点和较大孩子的值,将较大值的下标赋给largestlargest = arr[largest] > arr[index] ? largest : index;// 如果父节点已经是最大的,跳出循环if (largest == index) {break;}// 交换父节点和较大孩子的位置swap(arr, largest, index);// 更新当前节点的下标index = largest;// 计算新的左孩子的下标left = index * 2 + 1;}}/*** 交换数组中两个指定索引位置的元素。** @param arr 包含元素的数组* @param i 第一个元素的索引* @param j 第二个元素的索引*/public void swap(int[] arr, int i, int j) {// 使用临时变量交换两个元素的值int tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;}
过程分析
我们可以把堆排序的过程大致分解成两个大的步骤,建堆和排序。
建堆
我们首先将数组原地建成一个堆。所谓“原地”就是,不借助另一个数组,就在原数组上操作。建堆的过程,有两种思路。
第一种是在堆中插入一个元素。尽管数组中包含个数据,但是我们可以假设,起初堆中只包含一个数据,就是下标为1的数据。然后,我们调用插入操作,将下标从2到n的数据依次插入到堆中。这样我们就将包含n个数据的数组,组织成了堆。
第二种实现思路,跟第一种截然相反。第一种建堆思路的处理过程是从前往后处理数组数据,并且每个数据插入堆中时,都是从下往上堆化。而第二种实现思路,是从后往前处理数组,并且每个数据都是从上往下堆化。
逐层调整,直到整个数组满足堆的条件。
- 从最后一个非叶子节点开始,逐层向上进行 heapify 操作。
- 最后一个非叶子节点的索引为:floor((n-2)/2),其中 n 是数组的长度。
- 使用 heapify 调整以该节点为根的子树。
排序
建堆结束之后,数组中的数据已经是按照大顶堆的特性来组织的。数组中的第一个元素就是堆顶,也就是最大的元素。我们把它跟最后一个元素交换,那最大元素就放到了下标为n的位置。
当堆顶元素移除之后,我们把下标为n的元素放到堆顶,然后再通过堆化的方法,将剩下的n-1个元素重新构建成堆。堆化完成之后,我们再取堆顶的元素,放到下标是n一1的位置,一直重复这个过程,直到最后堆中只剩下标为1的一个元素,排序工作就完成了。
为什么快速排序要比堆排序性能好?
堆排序数据访问的方式没有快速排序友好
快速排序的数据访问方式:
-
快速排序是基于分治法(Divide and Conquer)的排序算法。它通过选择一个基准值(pivot),将数组划分为两个子数组,左侧子数组的元素小于或等于基准值,右侧子数组的元素大于或等于基准值。然后递归地对这两个子数组进行排序。在分割过程中,快速排序对数组中的元素进行连续的比较和交换,这种操作在内存中的数据访问模式通常是连续的,与现代计算机的缓存机制非常匹配。连续的内存访问使得缓存命中率更高,减少了内存访问的延迟,从而提高了排序的效率。
-
例如,当对一个已部分排序的数组进行快速排序时,基准值的选择和分割操作会使得数组被分割为较小的子数组,而这些子数组在内存中是连续存储的。对这些子数组进行递归排序时,缓存能够有效地预取(cache prefetch)这些连续的数据,减少了从主存中加载数据的次数,加快了排序的速度。
堆排序的数据访问方式:
-
堆排序依赖于堆数据结构,需要频繁地访问父节点和子节点。在堆排序过程中,数据的访问模式是随机的,无法充分利用内存的局部性原理。堆的父节点和子节点在内存中可能不是连续存储的,因此访问这些节点时,常常会导致缓存未命中。缓存未命中会增加内存访问的延迟,降低排序的效率。
-
例如,在调整堆结构时,需要比较父节点和子节点的值,并根据比较结果进行交换。这种访问模式使得数据在内存中的跳转较为频繁,无法像快速排序那样利用缓存的预取特性,导致性能下降。
对于同样的数据,在排序过程中,堆排序算法的数据交换次数要多于快速排序
快速排序的数据交换次数:
-
快速排序的平均时间复杂度为O(n log n),其数据交换次数相对较少。在快速排序的分割过程中,每个元素会被交换到正确的位置,但整个过程中交换的次数通常比堆排序少。快速排序的递归结构使得大部分元素的比较和交换发生在较小的子数组中,减少了不必要的数据移动。
-
例如,对一个随机分布的数组进行快速排序时,每次分割操作会使得元素被快速地放置在接近其最终位置的地方。这减少了后续排序过程中需要移动的数据量,从而降低了数据交换的总次数。
堆排序的数据交换次数:
-
堆排序的主要操作是构建堆和调整堆结构。在构建堆的过程中,需要多次比较和交换元素。每次插入或删除元素时,堆结构的维护需要进行多次比较和交换,以确保父节点的值大于或等于子节点的值(对于最大堆而言)。在堆排序的整个过程中,数据交换的次数相对较多。
-
例如,当构建一个最大堆时,每个元素都需要与它的子节点进行比较和交换。如果一个元素的值小于它的子节点中的较大者,就需要交换它们的位置,并继续对子节点进行调整。这个过程会涉及到多次数据交换,直到堆的性质被满足。在堆排序的调整过程中,这样的交换操作会频繁发生,导致数据交换次数增加,从而影响了排序的性能。
相关文章:
数据结构与算法面试专题——堆排序
完全二叉树 完全二叉树中如果每棵子树的最大值都在顶部就是大根堆 完全二叉树中如果每棵子树的最小值都在顶部就是小根堆 设计目标:完全二叉树的设计目标是高效地利用存储空间,同时便于进行层次遍历和数组存储。它的结构使得每个节点的子节点都可以通过简…...
《On Java进阶卷》阅读笔记(五)
第7章 IO系统 I/O流: IO有很多不同的来源和去处,如文件、控制台网络连接等,而且还涉及需求以很多种方式,如顺序读取、随机访问、缓冲、字符、按行读取、按字读取等。 Java8的函数式流相关的类和IO流之间并无关联。 IO流隐藏了…...

《代码随想录》刷题笔记——回溯篇【java实现】
文章目录 组合组合总和 III电话号码的字母组合组合总和组合总和II思路代码实现 分割回文串※思路字符串分割回文串判断效率优化※ 复原 IP 地址优化版本 子集子集 II使用usedArr辅助去重不使用usedArr辅助去重 递增子序列※全排列全排列 II重新安排行程题意代码 N 皇后解数独直…...

数值积分:通过复合梯形法计算
在物理学和工程学中,很多问题都可以通过数值积分来求解,特别是当我们无法得到解析解时。数值积分是通过计算积分区间内离散点的函数值来近似积分的结果。在这篇博客中,我将讨论如何使用 复合梯形法 来进行数值积分,并以一个简单的…...

AcWing——3624. 三值字符串
双指针解法 #include<iostream> #include<unordered_map> using namespace std; int main() {int n; cin >> n;while(n--){unordered_map<char, int> tree;string s; cin >> s;int ans 0x7fffffff; for(int i 0, j 0; j < (int)s.size();…...

【JavaEE进阶】验证码案例
目 🌲实现说明 🎄Hutool介绍 🌳准备工作 🌴约定前后端交互接口 🚩接口定义 🚩实现服务器后端代码 🚩前端代码 🚩整体测试 🌲实现说明 随着安全性的要求越来越⾼…...

Uniapp 短视频去水印解析工具开发实现
最近搞了一个有意思的小工具——短视频去水印解析器!这玩意儿可以把短视频中的水印给抹掉,还能提取视频、封面等资源。整个项目用了 Uniapp 开发,做完后体验了一下,发现还挺顺手。今天就来跟大家聊聊实现思路和代码细节~ 需求分析…...

计算机网络-八股-学习摘要
一:HTTP的基本概念 全称: 超文本传输协议 从三个方面介绍HTTP协议 1,超文本:我们先来理解「文本」,在互联网早期的时候只是简单的字符文字,但现在「文本」的涵义已经可以扩展为图片、视频、压缩包等&am…...

编程速递-庆祝Delphi诞生30周年!
庆祝Delphi 30周年纪念是一个特别的时刻。 回到1995年,也就是30年前,在微软Windows和互联网时代的曙光初现之时,Borland Delphi的创建者们无法想象,当时使用Borland Delphi构建的应用程序至今仍在运行——为全世界数十亿人服务。…...

每天五分钟深度学习框架pytorch:搭建谷歌的Inception网络模块
本文重点 前面我们学习了VGG,从现在开始我们将学习谷歌公司推出的GoogLeNet。当年ImageNet竞赛的第二名是VGG,而第一名就是GoogLeNet,它的模型设计拥有很多的技巧,这个model证明了一件事:用更多的卷积,更深的层次可以得到更好的结构 GoogLeNet的网络结构 如图所示就是Go…...

性能测试流程、主流性能工具
性能测试流程 性能测试流程 测试测试需求分析 性能测试计划和方案 测什么: 测试背景 测试目的 测试范围 谁来测: 进度和分工 交付清单 怎么测: 测试策略 性能测试用例设计 性能测试测试执行 性能分析和调优 性能测试报告 测试报告是…...

DeepSeek4j 已开源,支持思维链,自定义参数,Spring Boot Starter 轻松集成,快速入门!建议收藏
DeepSeek4j Spring Boot Starter 快速入门 简介 DeepSeek4j 是一个专为 Spring Boot 设计的 AI 能力集成启动器,可快速接入 DeepSeek 大模型服务。通过简洁的配置和易用的 API,开发者可轻松实现对话交互功能。 环境要求 JDK 8Spring Boot 2.7Maven/Gr…...

无耳科技 Solon v3.0.8 发布,Java 企业级应用开发框架
Solon 框架! Solon 是新一代,Java 企业级应用开发框架。是杭州无耳科技有限公司的“根级”开源项目(最近“杭州六小龙”很火啊,我们也是杭州的哦)。从零开始构建(No Spring、No Java-EE、No Servlet&#…...

【吾爱出品】针对红警之类老游戏适用WIN10和11的补丁cnc-ddraw7.1汉化版
针对红警之类老游戏适用WIN10和11的补丁cnc-ddraw7.1汉化版 链接:https://pan.xunlei.com/s/VOJ8PZd4avMubnDzHQAeZDxWA1?pwdnjwm# 直接复制到游戏安装目录,保持与游戏主程序同目录下。...
使用 playwright 自定义 js 下载的路径和文件名
遇到一个问题,点击按钮自动下载文件,路径和文件名都不能自定义,可以用 playwright 来解决这个问题 from playwright.sync_api import sync_playwright import os import time class ExcelDownloader: def __init__(self, download_pat…...

Kafka分区管理大师指南:扩容、均衡、迁移与限流全解析
#作者:孙德新 文章目录 分区分配操作(kafka-reassign-partitions.sh)1.1 分区扩容、数据均衡、迁移(kafka-reassign-partitions.sh)1.2、修改topic分区partition的副本数(扩缩容副本)1.3、Partition Reassign场景限流1.4、节点内副本移动到不…...

3.从零开始学会Vue--{{生命周期,工程化,组件化}}
1.生命周期钩子 1.是什么 生命周期 概念:就是一个Vue实例从创建 到 销毁 的整个过程 生命周期包括:① 创建 ② 挂载 ③ 更新 ④ 销毁 四个阶段 1.创建阶段:创建响应式数据 2.挂载阶段:渲染模板 3.更新阶段:修改…...
Python--网络编程
3. 网络编程与Socket 3.1 Socket基础 创建Socket import socket# TCP Socket tcp_socket socket.socket(socket.AF_INET, socket.SOCK_STREAM)# UDP Socket udp_socket socket.socket(socket.AF_INET, socket.SOCK_DGRAM)服务器端函数 函数描述bind((host, port))绑定…...
【java】方法的基本内存原理(栈和堆)
java内存主要分为栈和堆,方法相关的部分主要在栈内存里,每个方法调用时会在栈里创建一个栈帧,存放局部变量和方法执行的信息。执行完后栈帧被销毁,局部变量消失。而对象实例存在堆里,由垃圾回收器管理。 **Java方法内…...

SQLMesh 系列教程4- 详解模型特点及模型类型
SQLMesh 作为一款强大的数据建模工具,以其灵活的模型设计和高效的增量处理能力脱颖而出。本文将详细介绍 SQLMesh 模型的特点和类型,帮助读者快速了解其强大功能。我们将深入探讨不同模型类型(如增量模型、全量模型、SCD Type 2 等࿰…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...

C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...

MySQL的pymysql操作
本章是MySQL的最后一章,MySQL到此完结,下一站Hadoop!!! 这章很简单,完整代码在最后,详细讲解之前python课程里面也有,感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...
【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解
一、前言 在HarmonyOS 5的应用开发模型中,featureAbility是旧版FA模型(Feature Ability)的用法,Stage模型已采用全新的应用架构,推荐使用组件化的上下文获取方式,而非依赖featureAbility。 FA大概是API7之…...
使用python进行图像处理—图像滤波(5)
图像滤波是图像处理中最基本和最重要的操作之一。它的目的是在空间域上修改图像的像素值,以达到平滑(去噪)、锐化、边缘检测等效果。滤波通常通过卷积操作实现。 5.1卷积(Convolution)原理 卷积是滤波的核心。它是一种数学运算,…...