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

【算法基础】递归算法 - JAVA

一、递归基础

1.1 什么是递归算法

递归算法是一种通过函数调用自身来解决问题的方法。简单来说,就是"自己调用自己"。递归将复杂问题分解为同类的更简单子问题,直到达到易于直接解决的基本情况。

1.2 递归的核心要素

递归算法由两个关键部分组成:

  • 边界条件(终止条件):决定递归何时结束,避免无限递归
  • 递归步骤:函数调用自身处理较小规模的问题

1.3 递归的基本结构

public ReturnType recursiveMethod(Parameters params) {// 边界条件(终止条件)if (结束条件) {return 结束值;}// 递归步骤return recursiveMethod(减小规模的参数);
}

1.4 递归的复杂度分析

时间复杂度:递归算法的时间复杂度主要取决于以下因素:

  • 递归调用的次数
  • 每次递归中的非递归操作耗时

递归算法的时间复杂度通常可以用递推关系表示:

  • T(n) = a·T(n/b) + f(n)
    • T(n)是规模为n的问题的时间复杂度
    • a是每次递归调用的次数
    • n/b是子问题的规模
    • f(n)是除递归调用外的操作耗时

空间复杂度:递归算法的空间复杂度考虑两部分:

  • 递归调用栈空间:与递归深度成正比
  • 额外使用的数据结构空间

递归调用的栈空间通常是递归算法空间复杂度的主要部分,对于最大递归深度为D的算法,栈空间复杂度为O(D)。

二、简单递归示例

2.1 计算1到n的和

计算 1+2+3+...+n 的和是理解递归的最佳入门示例。

public class SumExample {public static void main(String[] args) {// 计算1到100的和System.out.println("1到100的和: " + getSum(100));}// 计算从1到n的和的递归方法public static int getSum(int n) {// 边界条件:当n为1时,直接返回1if (n == 1) {return 1;}// 递归步骤:n加上(n-1)的和return n + getSum(n - 1);}
}
递归执行过程详解

以计算getSum(5)为例,让我们看看递归的具体执行过程:

正向递归调用过程(自顶向下):

  1. 调用getSum(5),因为5 != 1,执行return 5 + getSum(4),暂停等待getSum(4)的结果
  2. 调用getSum(4),因为4 != 1,执行return 4 + getSum(3),暂停等待getSum(3)的结果
  3. 调用getSum(3),因为3 != 1,执行return 3 + getSum(2),暂停等待getSum(2)的结果
  4. 调用getSum(2),因为2 != 1,执行return 2 + getSum(1),暂停等待getSum(1)的结果
  5. 调用getSum(1),满足边界条件n == 1,直接return 1

反向结果返回过程(自底向上):

  1. getSum(1)返回1
  2. getSum(2)继续执行计算2 + 1 = 3,返回3
  3. getSum(3)继续执行计算3 + 3 = 6,返回6
  4. getSum(4)继续执行计算4 + 6 = 10,返回10
  5. getSum(5)继续执行计算5 + 10 = 15,返回15,计算完成

可以看到,递归过程包含两个阶段:

  • 分解阶段:将问题拆解为更小的子问题
  • 回溯阶段:从最简单情况开始,逐层向上计算结果

2.2 计算阶乘

阶乘是另一个简单的递归实例。n的阶乘定义为:n! = n * (n-1) * (n-2) * ... * 2 * 1

public class FactorialExample {public static void main(String[] args) {int n = 5;System.out.println(n + "的阶乘是: " + factorial(n));}// 计算阶乘的递归方法public static int factorial(int n) {// 边界条件if (n == 0 || n == 1) {return 1;}// 递归步骤return n * factorial(n - 1);}
}
递归执行过程

计算factorial(4)的步骤:

  1. factorial(4)4 * factorial(3)
  2. factorial(3)3 * factorial(2)
  3. factorial(2)2 * factorial(1)
  4. factorial(1) → 返回1(边界条件)
  5. factorial(2)2 * 1 = 2
  6. factorial(3)3 * 2 = 6
  7. factorial(4)4 * 6 = 24

时间复杂度分析

  • 阶乘递归函数的调用次数与输入n成正比
  • 每次函数调用中只有常数级别的操作
  • 总时间复杂度:O(n)

空间复杂度分析

  • 递归深度为n,每层递归使用常数级别的空间
  • 总空间复杂度:O(n)

2.3 斐波那契数列

斐波那契数列是一个经典递归案例,定义为:F(0)=0, F(1)=1, F(n)=F(n-1)+F(n-2) (n≥2)。

public class FibonacciExample {public static void main(String[] args) {int n = 7;System.out.println("斐波那契数列第" + n + "个数是: " + fibonacci(n));// 打印斐波那契数列的前10个数System.out.print("斐波那契数列前10个数: ");for (int i = 0; i < 10; i++) {System.out.print(fibonacci(i) + " ");}}// 计算斐波那契数列的递归方法public static int fibonacci(int n) {// 边界条件if (n == 0) {return 0;}if (n == 1) {return 1;}// 递归步骤return fibonacci(n - 1) + fibonacci(n - 2);}
}
特别注意

斐波那契数列的简单递归实现存在大量重复计算,效率较低。例如计算fibonacci(5)时,fibonacci(3)会被重复计算多次。这个问题在后面的优化部分会详细讨论。

时间复杂度分析

  • 未优化的递归斐波那契函数时间复杂度为O(2^n)
  • 这是因为每个递归调用会产生两个新的递归调用,形成二叉树结构
  • 树的高度为n,节点总数约为2^n-1

空间复杂度分析

  • 虽然会有大量函数调用,但递归栈的最大深度为n
  • 空间复杂度:O(n)

三、理解递归思维

3.1 递归思维的要点

递归思维的核心是把大问题分解成小问题:

  1. 相信函数能完成任务:假设对于较小规模的输入,函数已经能正确工作
  2. 确保边界情况正确:解决最简单的情况作为起点
  3. 确保递归逐步接近边界:每次递归,问题规模都应该变小

3.2 设计递归函数的步骤

  1. 确定函数的功能和参数:明确函数要完成什么任务,需要哪些参数
  2. 确定边界条件:找到最简单的情况,直接给出答案
  3. 确定递归关系:将原问题分解为更小规模的子问题
  4. 组合子问题结果:如何从子问题的解得到原问题的解

四、递归的实际应用

4.1 二分查找

二分查找是在有序数组中查找特定元素的高效算法,可以使用递归实现。

public class BinarySearchExample {public static void main(String[] args) {int[] arr = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19};int target = 11;int result = binarySearch(arr, target, 0, arr.length - 1);if (result != -1) {System.out.println("元素 " + target + " 在索引 " + result + " 处");} else {System.out.println("元素 " + target + " 不在数组中");}}// 递归实现的二分查找public static int binarySearch(int[] arr, int target, int left, int right) {// 边界条件:如果查找范围无效,返回-1if (left > right) {return -1;}// 计算中间索引int mid = left + (right - left) / 2;// 找到目标,返回索引if (arr[mid] == target) {return mid;}// 根据中间值与目标值的比较,递归查找左半部分或右半部分if (arr[mid] > target) {return binarySearch(arr, target, left, mid - 1);} else {return binarySearch(arr, target, mid + 1, right);}}
}
递归过程分析

以查找值11为例:

  1. binarySearch(arr, 11, 0, 9),计算mid=4,arr[4]=9 < 11,递归调用右半部分
  2. binarySearch(arr, 11, 5, 9),计算mid=7,arr[7]=15 > 11,递归调用左半部分
  3. binarySearch(arr, 11, 5, 6),计算mid=5,arr[5]=11 == 11,返回5(找到)

时间复杂度分析

  • 每次递归调用将搜索范围减半
  • 对于长度为n的数组,最多需要log₂n次递归调用
  • 每次递归中的操作是常数级别的
  • 总时间复杂度:O(log n)

空间复杂度分析

  • 递归深度最多为log₂n
  • 每层递归使用常数级别的额外空间
  • 总空间复杂度:O(log n)

4.2 求解全排列

全排列问题是递归的经典应用。给定一组不同的数字,求其所有可能的排列。

public class PermutationExample {public static void main(String[] args) {String str = "ABC";System.out.println("字符串\"" + str + "\"的全排列:");permute(str.toCharArray(), 0, str.length() - 1);}// 生成全排列的递归方法public static void permute(char[] arr, int start, int end) {// 边界条件:当只剩一个字符时,打印当前排列if (start == end) {System.out.println(String.valueOf(arr));return;}// 递归步骤:固定一个字符,对剩余字符进行全排列for (int i = start; i <= end; i++) {// 交换字符swap(arr, start, i);// 递归生成其余字符的排列permute(arr, start + 1, end);// 恢复原来的顺序(回溯)swap(arr, start, i);}}// 交换字符的辅助方法private static void swap(char[] arr, int i, int j) {char temp = arr[i];arr[i] = arr[j];arr[j] = temp;}
}
递归思路解释

以字符串"ABC"为例:

  1. 首先,我们固定第一个位置(索引0)的字符,然后对剩余字符递归生成全排列
    • 固定A:递归处理"BC"的排列
    • 固定B:递归处理"AC"的排列
    • 固定C:递归处理"AB"的排列
  2. 当递归到只剩一个字符时,我们已经生成了一个完整的排列

时间复杂度分析

  • 对于长度为n的字符串,共有n!种排列
  • 每生成一个排列需要O(n)的时间(打印或存储)
  • 总时间复杂度:O(n × n!)

空间复杂度分析

  • 递归深度为n,每层递归使用常数级别的额外空间
  • 如果考虑输出存储,需要O(n × n!)的空间
  • 仅递归调用栈的空间复杂度:O(n)

4.3 汉诺塔问题

汉诺塔是递归的经典问题,它很好地展示了递归如何简化看似复杂的问题。

public class TowerOfHanoiExample {public static void main(String[] args) {int n = 3; // 盘子数量System.out.println("移动" + n + "个盘子的步骤:");moveDisks(n, 'A', 'B', 'C');}// 移动汉诺塔盘子的递归方法public static void moveDisks(int n, char source, char auxiliary, char target) {// 边界条件:只有一个盘子时,直接从起始柱移到目标柱if (n == 1) {System.out.println("将盘子1从" + source + "移动到" + target);return;}// 递归步骤1:将n-1个盘子从起始柱移到辅助柱moveDisks(n - 1, source, target, auxiliary);// 移动最大的盘子从起始柱到目标柱System.out.println("将盘子" + n + "从" + source + "移动到" + target);// 递归步骤2:将n-1个盘子从辅助柱移到目标柱moveDisks(n - 1, auxiliary, source, target);}
}
递归思路解释

汉诺塔问题的美妙之处在于利用递归,我们可以将复杂的多盘子问题,分解为更简单的子问题:

  1. 将上面n-1个盘子从起始柱移到辅助柱
  2. 将最底下的盘子从起始柱移到目标柱
  3. 将辅助柱上的n-1个盘子移到目标柱

这里的关键是"相信"递归函数能够正确移动n-1个盘子,这样就能将复杂问题简化。

时间复杂度分析

  • 对于n个盘子,总共需要2^n-1步移动
  • 每层递归产生两个新的递归调用,直到达到基本情况
  • 总时间复杂度:O(2^n)

空间复杂度分析

  • 递归调用的最大深度为n
  • 每层递归使用常数级别的额外空间
  • 总空间复杂度:O(n)

五、递归优化技巧

5.1 记忆化递归(备忘录法)

斐波那契数列的简单递归实现效率很低,因为有大量重复计算。使用记忆化技术可以显著提高效率。

public class MemoizedFibonacci {public static void main(String[] args) {int n = 40;// 比较普通递归和记忆化递归的时间long startTime = System.currentTimeMillis();System.out.println("斐波那契(" + n + ") = " + fibonacciMemoized(n));long endTime = System.currentTimeMillis();System.out.println("记忆化递归耗时: " + (endTime - startTime) + "毫秒");}// 使用记忆化的斐波那契数列递归方法public static long fibonacciMemoized(int n) {// 创建备忘录数组,保存计算过的结果long[] memo = new long[n + 1];return fibHelper(n, memo);}private static long fibHelper(int n, long[] memo) {// 边界条件if (n <= 1) {return n;}// 如果已经计算过,直接返回结果if (memo[n] != 0) {return memo[n];}// 递归计算并保存结果memo[n] = fibHelper(n - 1, memo) + fibHelper(n - 2, memo);return memo[n];}
}

记忆化递归的关键思想

  • 用数组或HashMap存储已计算的值
  • 每次函数调用前先检查是否已计算
  • 只计算未知结果并保存

复杂度优化分析

  • 时间复杂度:从指数级O(2^n)降低到线性O(n)
    • 每个斐波那契数只计算一次
    • 总共计算n+1个数,每次计算是常数时间
  • 空间复杂度:O(n)
    • 需要额外的长度为n+1的数组存储计算结果
    • 递归调用栈深度为O(n)

5.2 尾递归优化

尾递归是一种特殊的递归形式,它在函数返回时不做额外计算,直接返回递归调用的结果。这种形式更容易被编译器优化。

以阶乘计算为例:

public class TailRecursionExample {public static void main(String[] args) {int n = 5;System.out.println(n + "的阶乘: " + factorial(n));System.out.println(n + "的阶乘(尾递归): " + factorialTail(n, 1));}// 普通递归实现阶乘public static int factorial(int n) {if (n <= 1) {return 1;}return n * factorial(n - 1);  // 在递归调用后还需要乘以n}// 尾递归实现阶乘public static int factorialTail(int n, int result) {if (n <= 1) {return result;}return factorialTail(n - 1, n * result);  // 直接返回递归调用的结果}
}

尾递归的特点

  • 递归调用是函数的最后一个操作
  • 递归调用的返回值直接作为函数的返回值
  • 不需要额外的计算或状态保存

注意:虽然Java不会自动优化尾递归,但理解尾递归仍有助于编写更高效的代码,尤其是在支持尾递归优化的语言中。

5.3 避免重复计算

在一些递归算法中,避免重复计算是提高效率的关键。以下是几种常见技巧:

  1. 使用缓存:与记忆化类似,保存中间结果
  2. 参数传递:通过参数传递已计算的信息,避免重复计算
  3. 状态记录:使用额外变量记录状态,避免重复检查

六、递归的常见陷阱与解决方法

6.1 无限递归

如果忘记设置正确的终止条件,或递归时参数未正确减小,可能导致无限递归,最终导致栈溢出。

解决方法

  • 确保每次递归都朝着终止条件推进
  • 检查边界条件是否全面且正确
  • 考虑添加最大递归深度限制

6.2 栈溢出

递归层数过多会导致栈溢出(StackOverflowError)。

解决方法

  • 优化递归算法,减少递归深度
  • 将递归转换为迭代
  • 使用尾递归优化(在支持的语言中)
  • 增大JVM栈大小(-Xss参数)

6.3 重复递归调用

在某些算法中,同一参数的递归调用可能被多次执行,导致不必要的计算。

解决方法

  • 使用记忆化技术(备忘录模式)
  • 优化算法结构,避免重复调用
  • 考虑使用自底向上的迭代方法

七、递归转迭代

有时,递归算法可能面临效率或栈空间限制,此时可以考虑将递归转换为迭代。

7.1 斐波那契数列的迭代实现

public static long fibonacciIterative(int n) {if (n <= 1) {return n;}long fib = 0;long prev1 = 1;long prev2 = 0;for (int i = 2; i <= n; i++) {fib = prev1 + prev2;prev2 = prev1;prev1 = fib;}return fib;
}

复杂度分析

  • 时间复杂度:O(n) - 只需一次遍历
  • 空间复杂度:O(1) - 只使用了常数个变量

7.2 阶乘的迭代实现

public static int factorialIterative(int n) {int result = 1;for (int i = 2; i <= n; i++) {result *= i;}return result;
}

复杂度分析

  • 时间复杂度:O(n) - 需要n-1次乘法运算
  • 空间复杂度:O(1) - 只使用了常数个变量

递归转迭代的一般思路

  1. 使用栈或其他数据结构模拟递归过程
  2. 找出算法中的状态变量
  3. 通过循环代替递归调用
  4. 手动管理状态变更

转换后的优势

  • 避免栈溢出风险
  • 降低函数调用开销
  • 通常具有更低的空间复杂度
  • 在某些情况下可提高执行速度

八、总结

8.1 递归的核心要点

  • 分而治之:将大问题分解为相似的小问题
  • 边界条件:解决最基本情况的方法
  • 递归关系:大问题与小问题之间的联系

8.2 何时使用递归

适合使用递归的情况:

  • 问题可以自然地分解为相似的子问题
  • 问题的规模会逐渐减小
  • 数据结构具有递归性质(如树、图)
  • 代码简洁度比极限性能更重要

不适合使用递归的情况:

  • 递归深度可能非常大
  • 对执行效率有严格要求
  • 问题可以简单地用迭代解决

8.3 递归思维的价值

掌握递归思维不仅是掌握一种编程技巧,更是一种解决问题的思路:

  • 学会将复杂问题分解
  • 培养"相信函数能工作"的思维方式
  • 理解如何从子问题构建完整解决方案

递归是算法设计中的强大工具,掌握它将帮助你解决许多看似复杂的问题。通过从简单例子开始,逐步理解递归的核心概念,再结合优化技巧,你将能够写出既高效又优雅的递归算法。

相关文章:

【算法基础】递归算法 - JAVA

一、递归基础 1.1 什么是递归算法 递归算法是一种通过函数调用自身来解决问题的方法。简单来说&#xff0c;就是"自己调用自己"。递归将复杂问题分解为同类的更简单子问题&#xff0c;直到达到易于直接解决的基本情况。 1.2 递归的核心要素 递归算法由两个关键部…...

连续变量与离散变量的互信息法

1. 互信息法简介 互信息&#xff08;Mutual Information, MI&#xff09; 是一种衡量两个变量之间相互依赖程度的统计量&#xff0c;它来源于信息论。互信息可以用于评估特征与目标变量之间的相关性&#xff0c;无论这些变量是连续的还是离散的。互信息法是一种强大的特征选择…...

java_Lambda表达式

1、背景 lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。lambda表达式就和方法一样样&#xff0c;它提供了一个正常的参数列表和一个使用这些参数的主体&#xff08;body&#xff0c;可以是一个表达式和一个代码块&#xff09;。La…...

Python Cookbook-6.17 NuIl对象设计模式的实现

任务 你想减少代码中的条件声明&#xff0c;尤其是针对特殊情况的检查。 解决方案 一种常见的代表“这里什么也没有”的占位符是 None&#xff0c;但我们还可以定义一个类&#xff0c;其行为方式和这种占位符相似&#xff0c;而且效果更好: class Null(object):Null对象总是…...

Java接口全面教程:从入门到精通

目录 接口的基本概念 接口的特性 1. 访问修饰符 2. 接口中的常量 3. 接口中的方法 3.1 抽象方法&#xff08;传统用法&#xff09; 3.2 默认方法&#xff08;Java 8 引入&#xff09; 3.3 静态方法&#xff08;Java 8 引入&#xff09; 3.4 私有方法&#xff08;Java …...

Power Query精通指南3:数据库(查询折叠与数据隐私)、批量合并文件、自定义函数

文章目录 九、批量合并文件9.1 案例背景9.2 合并文件的标准流程9.3 示例&#xff1a;合并文件9.3.1 连接到文件夹9.3.1.1 连接到本地 / 网络文件夹9.3.1.2 连接到 SharePoint 文件夹9.3.1.3 连接到 OneDrive for Business9.3.1.4 连接到其他文件系统 9.3.2 筛选文件9.3.3 合并文…...

Python 学习

这里主要是为了记录我学习Python的过程&#xff0c;更多是使我规范书写Pyhton语言&#xff01; 1. 第一章 Python 定义&#xff1a;一种解释型的语言&#xff0c;区别于其他的高级语言&#xff0c;逐行翻译进行执行。 过程&#xff1a;首先编写编程语言&#xff0c;利用Pytho…...

生成式 AI 的优势

在科技飞速发展的今天,人工智能已经不再是一个遥不可及的概念,而是逐渐渗透到我们生活的方方面面。其中,生成式 AI 更是如同一颗璀璨的新星,在人工智能的浩瀚星空中闪耀着独特的光芒。它究竟有哪些令人瞩目的优势,又为何会成为我们这个时代无法忽视的存在呢? 生成式 AI …...

Hal库下备份寄存器

首先要确保有外部电源给VBAT供电 生成后应该会有这两个文件&#xff08;不知道为什么生成了好几次都没有&#xff0c;复制工程在试一次就有了&#xff09; 可以看到stm32f407有20个备份寄存器 读写函数 void HAL_RTCEx_BKUPWrite(RTC_HandleTypeDef *hrtc, uint32_t Backup…...

P1537 数字反转(升级版)详解

这个题目还是对于新手比较锻炼思维严谨性的&#xff0c;我认为是在我做过的一些题目中&#xff0c;此题算上等马 先看题目 我先说明我自己的思路&#xff0c;以及这个题目你需要特别注意的地方 1&#xff0c;数字反转&#xff0c;①可用<algorithm>库里面的reverse函数…...

operator 可以根据需要重载 == 运算符进行比较

要将 vector<AppInfo> 类型的 A 和 B 两个容器进行比较&#xff0c;并且当 B 中有 A 中没有的元素时&#xff0c;插入到数据库中&#xff0c;你可以通过以下步骤实现&#xff1a; 比较元素&#xff1a;遍历 vector<B>&#xff0c;检查每个元素是否在 vector<A&…...

网格不迷路:用 CSS 网格生成器打造完美布局

前言 你是否曾因写错 grid-template-areas 而捶键盘?是否在面对千层嵌套的复杂布局时,瞬间怀疑人生,甚至思考要不要转行去卖奶茶?别慌,CSS 网格生成器闪亮登场,像拼乐高一样,帮你轻松搭建网页结构,还能自动输出干净代码,堪称“前端界的乐高大师”。让我们放下枯燥的代…...

Go小技巧易错点100例(二十八)

本期分享&#xff1a; 1. runtime.Caller(1)获取调用者信息 2. for循环 select{}语法 正文&#xff1a; runtime.Caller(1)获取调用者信息 在 Go 语言中&#xff0c;runtime.Caller(1) 是 runtime 包提供的一个函数&#xff0c;用于获取当前 goroutine 的调用堆栈中的特定…...

Java变量简介

Java变量 -为什么需要变量? 一个程序就是一个世界 变量是程序的基本组成单位 不论是使用哪种高级程序语言编写程序,变量都是其程序的基本组成单位,比如: //变量有三个基本要素(类型+名称+值) class Test{public static void main(String [largs){int a=1;int b=3:b=89;Syst…...

Java快速上手之实验六

1. 编写ItemEventDemo.java&#xff0c;当选中或取消选中单选钮、复选钮和列表框时显示所选的结果。 2&#xff0e;编写GUIExample.java&#xff0c;当选中或取消选中单选钮、复选钮时在标签中显示相应结果。 import javax.swing.*; import java.awt.*; import java.awt.event.…...

【算法应用】基于灰狼算法优化深度信念网络回归预测(GWO-DBN)

目录 1.深度信念网络&#xff08;Deep Belief Networks, DBNs&#xff09;2.灰狼算法GWO原理3.结果展示4.参考文献5.代码获取6.读者交流 1.深度信念网络&#xff08;Deep Belief Networks, DBNs&#xff09; 深度信念网络&#xff08;Deep Belief Networks, DBNs&#xff09;是…...

基于Spring Boot实现STDIO通信的MCP Server与验证

STDIO 是一种基于标准输入输出(Standard Input/Output)的本地通信机制,旨在实现客户端与服务端之间的高效交互。 STDIO 是 MCP 协议支持的传输方式之一,通过操作系统的管道机制(stdin/stdout)进行数据传输,适用于客户端与服务端在同一台机器上的本地通信场景。 本篇基于…...

springboot基于推荐算法的景点推荐系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 本景点推荐系统采用B/S架构&#xff0c;数据库是MySQL&#xff0c;网站的搭建与开发采用了先进的Java进行编写&#xff0c;使用了协同过滤推荐算法和Spring Boot框架。该系统从两个对象&#xff1a;由管理员和用户来对系统进行设计构建。前台主要功能包括&#xff1a;用户…...

【LeetCode Hot100】栈篇

前言 本文用于整理LeetCode Hot100中题目解答&#xff0c;因题目比较简单且更多是为了面试快速写出正确思路&#xff0c;只做简单题意解读和一句话题解方便记忆。但代码会全部给出&#xff0c;方便大家整理代码思路。 20. 有效的括号 一句话题意 验证括号序列有效性。 一句话…...

IO模型和多路复用

一、IO模型的基础理解 什么是IO? IO全称是 Input/Output(输入/输出),在计算机科学里主要指程序与外部设备(硬盘、网络、用户终端等)进行数据交换的操作。首要特点是: IO通常很慢(从CPU和内存的视角看)经常需要等待外部设备响应1. 为什么要谈IO模型? 当一个程序需要…...

私人医生通过AI分析基因数据,是否有权提前告知癌症风险?

首席数据官高鹏律师团队编著 在精准医疗的浪潮中&#xff0c;私人医生借助AI技术解析基因数据、预判癌症风险&#xff0c;已成为高端医疗服务的“隐形标配”。然而&#xff0c;这一技术的光环之下&#xff0c;潜藏着法律与伦理的复杂博弈——医生是否有权基于AI的基因分析提前…...

day 11 超参数调整

一、内参与外参&#xff08;超参数&#xff09; 内参是模型为了适应训练数据而自动调整的&#xff0c;是模型内部与训练数据紧密相关的因素&#xff0c;不同的训练数据会导致模型学习到不同的参数值&#xff0c;这些参数在模型训练完成后就固定下来。 超参数是在模型训练前需…...

纯Java实现STDIO通信的MCP Server与客户端验证

在 MCP 协议中通过 STDIO(标准输入/输出)通信 是一种进程间通信(IPC)方式,服务器与客户端通过标准输入(stdin)和标准输出(stdout)交换数据。 关于STDIO 详细介绍以及如何基于Spring Boot项目实现 STDIO 的MCP服务器 以及如何调用和验证服务器可以参考: 基于Spring …...

Vue3学习笔记2——路由守卫

路由守卫 全局 router.beforeEach((to, from, next) > {})router.afterEach((to, from, next) > {}) 组件内守卫 beforeRouteEnter((to, from, next) > {})beforeRouteUpdate((to, from, next) > {})beforeRouteLeave((to, from, next) > {}) 路由独享 be…...

Three.js在vue中的使用(二)-加载、控制

在 Vue 中使用 Three.js 加载模型、控制视角、添加点击事件是构建 3D 场景的常见需求。下面是一个完整的示例&#xff0c;演示如何在 Vue 单文件组件中实现以下功能&#xff1a; 使用 GLTFLoader 加载 .glb/.gltf 模型添加 OrbitControls 控制视角&#xff08;旋转、缩放、平移…...

【堆】最大堆、最小堆以及GO语言的实现

堆是计算机科学中一种特别的完全二叉树结构&#xff0c;在优先队列、图算法和排序算法中有广泛应用。本文将从概念、原理和实现等方面详细介绍堆这一重要的数据结构。 1. 堆的基本概念 1.1 什么是堆&#xff1f; 堆&#xff08;Heap&#xff09;是一种特殊的完全二叉树&…...

动态规划之路劲问题3

解析题目&#xff1a; 跟之前路径题目大概一样&#xff0c;从左上角到右下角&#xff0c;每一步只能向下或者向右&#xff0c;而且每次走出来血量必须大于0&#xff08;注意这一点&#xff0c;否则容易导致每次出来可能小于0就可能错&#xff09; 算法分析&#xff1a; 状态…...

学习黑客网络安全法

在正式“开荒”各种黑客工具前&#xff0c;Day 4 的任务是给自己装上一副合规与伦理的“护身铠”。这一小时你将弄懂——做渗透想合法必须先拿授权、哪些法律条款碰不得、等保 2.0 与关基条例为何对企业像副“主线任务”&#xff1b;同时动手把这些要点制成一张“法律速查卡”&…...

节流 和 防抖的使用

节流&#xff08;Throttle&#xff09;是一种常用的性能优化技术&#xff0c;用于限制函数的执行频率&#xff0c;确保在一定时间内只执行一次。它常用于处理浏览器事件&#xff08;如滚动、窗口调整大小、鼠标移动等&#xff09;&#xff0c;以避免因事件触发过于频繁而导致的…...

关于项目中优化使用ConcurrentHashMap来存储锁对象

方案介绍 在开发用户创建私有空间功能时&#xff0c;我们的规则是一个用户最多只能创建一个私有空间。 在最初方案中&#xff0c;我是采用字符串常量池的方式存储锁对象useID。通过intern方法保证 同一用户ID的锁 唯一性。这一方案存在的问题是&#xff1a; 随着userId越来越…...

Java 网络安全新技术:构建面向未来的防御体系

一、Java 安全架构的演进与挑战 1.1 传统安全模型的局限性 Java 平台自 1995 年诞生以来&#xff0c;安全机制经历了从安全管理器&#xff08;Security Manager&#xff09;到 Java 平台模块系统&#xff08;JPMS&#xff09;的演进。早期的安全管理器通过沙箱模型限制不可信…...

【在Spring Boot中集成Redis】

在Spring Boot中集成Redis 依赖在application.yml中配置Redis服务地址创建Redis配置类缓存工具类使用 依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency&…...

昇腾的昇思MindSpore是什么?跟TensorFlow/PyTorch 等第三方框架有什么区别和联系?【浅谈版】

昇腾的昇思 MindSpore 是华为自主研发的全场景深度学习框架&#xff0c;旨在覆盖从科研到工业落地的全流程&#xff0c;支持云、边缘、手机等多种硬件场景的部署。它与 TensorFlow、PyTorch 等第三方框架既有相似性&#xff0c;也有明显差异。 一、昇思 MindSpore 的核心特点 全…...

MySQL进阶(三)

五、锁 1. 概述 锁是计算机协调多个进程或线程并发访问某一资源的机制&#xff08;避免争抢&#xff09;。 在数据库中&#xff0c;除传统的计算资源&#xff08;如 CPU、RAM、I/O 等&#xff09;的争用以外&#xff0c;数据也是一种供许多用户共享的资源。如何保证数据并发…...

Java面试趣事:从死循环到分段锁

互联网大厂Java开发岗终面现场 面试官&#xff08;推了推黑框眼镜&#xff09;&#xff1a;马小帅是吧&#xff1f;先说下HashMap扩容机制&#xff1f; 马小帅&#xff08;抓耳挠腮&#xff09;&#xff1a;这我知道&#xff01;默认初始容量16&#xff0c;默认负载因子0.75..…...

健康养生新主张

健康养生并非遥不可及的高深学问&#xff0c;摒弃中医理念&#xff0c;从生活细节入手&#xff0c;同样能实现身心的良好养护&#xff0c;开启活力满满的生活。​ 水是生命之源&#xff0c;科学饮水对养生意义重大。每天饮用 1500 - 2000 毫升的水&#xff0c;可维持身体正常的…...

合成复用原则(CRP)

非常好&#xff01;你已经学习了好几个设计原则&#xff0c;现在我们来讲解合成复用原则&#xff08;Composite Reuse Principle, CRP&#xff09;——它和继承是常被比较的一对“重用方式”。 &#x1f9e0; 一句话定义 合成复用原则&#xff08;CRP&#xff09;&#xff1a;尽…...

基于PyTorch的食物图像分类实战:从数据处理到模型训练

基于PyTorch的食物图像分类实战&#xff1a;从数据处理到模型训练 在深度学习领域&#xff0c;图像分类是一个经典且应用广泛的任务。无论是在电商平台的商品分类、医疗影像诊断&#xff0c;还是在农业作物识别等场景中&#xff0c;图像分类模型都发挥着重要作用。本文将以食物…...

在pycharm profession 2020.3将.py程序使用pyinstaller打包成exe

一、安装pyinstaller 在pycharm的项目的Terminal中运行pip3 install pyinstaller即可。 安装后在Terminal中输入pip3 list看一下是否成功 二、务必在在项目的Terminal中输入命令打包&#xff0c;命令如下&#xff1a; python3 -m PyInstaller --noconsole --onefile xxx.py …...

基于Springboot旅游网站系统【附源码】

基于Springboot旅游网站系统 效果如下&#xff1a; 系统登陆页面 系统主页面 景点信息推荐页面 路线详情页面 景点详情页面 确认下单页面 景点信息管理页面 旅游路线管理页面 研究背景 随着互联网技术普及与在线旅游消费习惯的深化&#xff0c;传统旅游服务模式面临效率低、…...

Linux操作系统从入门到实战(五)详细讲解Linux权限概念

Linux操作系统从入门到实战&#xff08;五&#xff09;详细讲解Linux权限概念 前言一、Linux中两种用户1.1 超级用户&#xff08;root&#xff09;1.2 普通用户1.3 切换用户命令 二、Linux权限管理2.1 文件访问者的分类&#xff1a;谁能访问文件&#xff1f;2.2 文件类型2.3 基…...

[方法论]软件工程中的设计模式:从理论到实践的深度解析

文章目录 软件工程中的设计模式&#xff1a;从理论到实践的深度解析引言&#xff1a;为什么需要设计模式&#xff1f;第一部分&#xff1a;设计模式的核心原则1. SOLID 原则&#xff08;面向对象设计的五大基石&#xff09;2. 其他关键思想 第二部分&#xff1a;创建型模式&…...

测试基础笔记第十八天

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、web自动化1.xpath定位1.属性定位2.属性与逻辑结合3.属性和层级结合 2.常见元素定位方法&#xff08;面试题&#xff09;3.常见元素定位失败原因4.cookie1.验证码…...

深度学习系统学习系列【2】之人工神经网络(ANN)

文章目录 说明人工神经网络概述基本单位&#xff08;神经元模型&#xff09;神经元基本模型线性模型与激活函数多层神经网络人工神经网络的注意内容 人工神经网络的阶段划分训练阶段输入、输出数据量化的设计权值向量W和偏置b的设计 预测阶段 人工神经网络核心算法梯度下降算法…...

《解锁Windows下GCC升级密码,开启高效编程新旅程》

《解锁Windows下GCC升级密码,开启高效编程新旅程》 为什么要升级 Windows 下的 GCC 版本? 在软件开发的动态领域中,GCC 作为一款卓越的编译器,在 Windows 环境下的升级有着重要意义,其影响深远且广泛。 从性能优化的角度来看,新版 GCC 往往在编译速度上有显著提升。随着…...

**Java面试大冒险:谢飞机的幽默与技术碰撞记**

互联网大厂Java求职者面试&#xff1a;一场严肃与搞笑交织的技术盛宴 场景&#xff1a; 互联网大厂面试间 人物&#xff1a; 面试官&#xff1a; 一位严肃的资深架构师&#xff0c;对技术要求严格。谢飞机&#xff1a; 一位搞笑的程序员&#xff0c;技术实力参差不齐。 第一…...

软件检测价格受多种因素影响,你了解多少?

软件检测价格受到多种因素的影响&#xff0c;这对企业来说至关重要&#xff0c;对开发者来说也至关重要&#xff0c;了解软件检测价格能够让企业和开发者更好地进行预算&#xff0c;能够让企业和开发者更好地进行决策。以下为你深入分析相关内容。 检测范围影响软件检测范围的…...

学习黑客风险Risk

一眼纵览 今天 Day 5 我们用 60 分钟打通「风险管理快闪副本」——先用漫画式视角速读两个国际标准&#xff08;NIST & ISO/IEC 27005&#xff09;&#xff0c;再把抽象概念变身为炫彩 Risk Heat Map&#xff0c;最后亲手填一张迷你 风险登记簿。学完你将能&#xff1a; 讲…...

scikit-learn在监督学习算法的应用

shiyonguyu大家好&#xff0c;我是我不是小upper&#xff01;最近行业大环境不是很好&#xff0c;有人苦恼别人都开始着手项目实战了&#xff0c;自己却还卡在 scikit-learn 的代码语法上&#xff0c;连简单的示例运行起来都磕磕绊绊。确实&#xff0c;对很多机器学习初学者来说…...

BG开发者日志505:项目总体情况

1、从2024年12月中旬启动&#xff0c;到4月底gameplay部分开发完毕&#xff0c;已经四个半月过去了。 其中大部分内容是3、4两个月中完成的&#xff0c;量产阶段。 预计6月初参加新品节&#xff0c;6月中旬发售&#xff08;比原计划7月中旬提前一个月&#xff09;。 --------…...