【动态规划】斐波那契数列模型

news2024/11/26 0:47:02

冻龟算法系列之斐波那契数列模型

在这里插入图片描述

文章目录

  • 【动态规划】斐波那契数列模型
    • 1. 第N个泰波那契数
      • 1.1 题目解析
      • 1.2 算法原理
        • 1.2.1 状态表示
        • 1.2.2 状态转移方程
        • 1.2.3 初始化
        • 1.2.4 填表顺序
        • 1.2.5 返回值
      • 1.3 编写代码
      • 1.4 空间优化
    • 2. 三步问题
      • 2.1 题目解析
      • 2.2 算法原理
        • 2.2.1 状态表示
        • 2.2.2 状态转移方程
        • 2.2.3 初始化
        • 2.2.4 填表顺序
        • 2.2.5 返回值
      • 2.3 编写代码
    • 3. 使用最小花费爬楼梯
      • 3.1 题目解析
      • 3.2 算法原理
        • 3.2.1 状态表示
        • 3.2.2 状态转移方程
        • 3.2.3 初始化
        • 3.2.4 填表顺序
        • 3.2.5 返回值
      • 3.3 编写代码
      • 3.4 解法2:以某节点为起点
        • 3.4.1 得到状态转移方程
        • 3.4.2 初始化
        • 3.4.3 编写代码
    • 4. 解码方法
      • 4.1 题目解析
      • 4.2 算法原理
        • 4.2.1 状态表示
        • 4.2.2 状态转移方程
        • 4.2.3 初始化
        • 4.2.4 填写顺序
        • 4.2.5 返回值
      • 4.3 编写代码
      • 4.4 代码简化技巧

【动态规划】斐波那契数列模型

动态规划(英语:Dynamic programming,简称 DP),是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题。

至于这个模型是什么意思,我觉得你往下看下去,自主感受那些相似之处,才不会懵逼~

而本文为动态规划系列的第一篇,所以在动态规划做题步骤上会比较分明,接下来的几道例题,让我们边实战边学吧!
文章解题的大的流程为:

  1. 题目解析
  2. 算法原理(可能会有其他解法,在这个系列只讲解动态规划的思想)
  3. 编写代码
  4. 空间优化
    • 动态规划一般很难,做出来已经不错了,所以主要重心放在解题上
    • 本文第一道题,把所有流程都会过一遍,所以会讲,而在讲背包问题之前,都不会出现这个步骤
    • 因为动态规划一般会比较浪费空间,所以才有这个空间优化的必要

1. 第N个泰波那契数

传送门:力扣1137

题目:

在这里插入图片描述

1.1 题目解析

在这里插入图片描述

所以有以下示例:

   [

同理:

在这里插入图片描述

1.2 算法原理

算法原理分析的过程分五步:

  1. 画出dp表并确定dp表**状态表示**
  2. 根据状态表示确定**状态转移方程**
  3. **初始化**dp表
  4. 确定**填表顺序**
  5. 确定**返回值**

由于是第一道题,所以下面是细致讲解!

  • 但是因为这道题比较简单,一些方面没有涉及到,但是对于初学者,这样才最友好,因为本题重点就是熟悉做题流程!
  • 在后续的学习中,反复积累经验,了解方方面面,再依此基础上去刷题,才能学好动态规划!

1.2.1 状态表示

什么是状态表示

这就涉及动态规划的核心:dp表

  • 这个dp表的建立是不固定的,可能是一维数组,二维数组等等…

而dp表中的元素与其对应下标有什么关系,即这个元素的**含义**,或者说是这个元素所处的“状态”(抽象笼统的说法)

  • 而其中,我们要的答案,就是dp表其中的一个元素

例子:

  • 开心or不开心,是状态
  • 1下标元素的含义就是滑稽老铁开心

在这里插入图片描述

  • 当然,dp表代表的状态一般不是对立的,而是这样的

在这里插入图片描述

  • 而之后我们可以 以“含义”去理解

而设立的状态表示不一样,也意味着思路不一样,即不同的解法~

  • 想到一个,然后就去试试能不能解…
  • 做多点题,这一步准确率会高点~

怎么来?

  1. 经验 + 题目要求
  2. 分析问题的过程中,发现重复的子问题,抽象成状态表示(暂时不讲)

而这道题,我们只需建立一个一维dp表(大小为n + 1):

  1. 题目要求:返回第n个泰波那契数的值
  2. 经验:dp[n]应该就是答案

在这里插入图片描述

即dp[i]表示:第 i 个泰波那契数的值

1.2.2 状态转移方程

什么是**状态转移方程**:

  • 就是,dp[i] = … ;
  • 而这个方程可能有条件,分支,循环等复杂的逻辑…

而dp[i]是由dp表其他下标对应的值来推导出来的

  • 比如,dp[i]之前或者之后的值可以推出dp[i]

例子:

  • 开心是会传染的:

在这里插入图片描述

而本题的动态转移方程就是:

dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];

  • 难的方程怎么推导?
    • 遇到再说
    • 现在纸上谈兵没有意义

对应动态规划的题目而言,前两步是最最重要的,也就是百分之99已完成~

接下来就是一些细节的处理

1.2.3 初始化

  • 很明显,通过已知推未知的过程中,会遇到一些数组越界等问题…,而初始化就是为了解决这些问题

本题为:

  • i - 1,i - 2,i - 3要有意义,则i要大于等于3
  • 所以在初始化的时候,手动给dp[0]、dp[1]、dp[2]赋值(赋值之前要确保不越界,越界就直接返回即可)

1.2.4 填表顺序

我们通过已知推未知,这里的已知可能是i之前或者i之后,这就分为了简单的两个大方向(一维dp表)
在这里插入图片描述

本题为:顺序1

  • 要保证填这个下标的时候,需要用到的其他下标,是已经在此之前填了的!
    • 即,计算过了的

1.2.5 返回值

根据题目要求 + 状态表示得出返回值

本题为:dp[n]

  • 注意下标确定是哪个
  • 可以说返回值和状态表示是同时确定的

1.3 编写代码

编写代码分为固定的四步:

  1. 创建dp表
  2. 初始化
  3. 算法主体(填表)
  4. 返回值
class Solution {
    public int tribonacci(int n) {
        //1. 创建dp表
        int[] dp = new int[n + 1];
        //2. 初始化
        if(n == 0) {
            return 0;
        }
        if(n == 1 || n == 2) {
            return 1;
        }
        dp[0] = 0;
        dp[1] = 1;
        dp[2] = 1;
        //3. 填表
        for(int i = 3; i < n + 1; i++) {
            dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];
        }
        //4. 返回值
        return dp[n];
    }   
}
  1. 由于我们要访问到dp[n]并返回,所以dp表的大小为n + 1
  2. 初始化之前要进行判断,否则会数组越界
  3. 通过状态转移方程来填表
  4. 返回dp[n]

在这里插入图片描述

在提交之前测试一下,可以自行输入实例去测~

在这里插入图片描述
在这里插入图片描述

可见空间复杂度和时间复杂度都为O(N)

1.4 空间优化

通过“滚动数组”去进行空间优化

  • O(N2) => O(N)
  • O(N) => O(1)

算法修改思想:

  • 计算dp[i]其实只需要用到dp[i - 1] + dp[i - 2] + dp[i - 3],其实一直都是用三块空间,所以我们只需要反复使用则三块空间就行了

在这里插入图片描述

class Solution {
    public int tribonacci(int n) {
        //1. 空间优化
        int[] src = new int[3];
        src[0] = 0;
        src[1] = 1;
        src[2] = 1;
        
        //2. 初始化
        if(n == 0) {
            return 0;
        }
        if(n == 1 || n == 2) {
            return 1;
        }
        int ret = 0;
        
        //3. 填表
        for(int i = 3; i < n + 1; i++) {
            ret = src[0] + src[1] + src[2];
            src[0] = src[1];
            src[1] = src[2];
            src[2] = ret;
        }
        //4. 返回值
        return ret;
    }   
}

赋值操作则是这样的:
在这里插入图片描述

则就从头部挪,而不是从尾部挪

从头部开始挪:

  • 正常

在这里插入图片描述

从尾部开始挪:

  • 异常,最终三个值都为2

在这里插入图片描述

在这里插入图片描述

第一道题就这样愉快的结束了,想必你已经了解了动态规划作答的流程了,接下来就是做几道题,走几遍流程,好好感受它“动归思想”~

2. 三步问题

传送门:面试题08.01.

题目:

在这里插入图片描述

2.1 题目解析

在这里插入图片描述

  • 小明一次可以跨1、2、3步~

输入一个数n,求出从0到n的爬法数

通过示例探索规律:
在这里插入图片描述

  • 并且结果要模上1e9 + 7!
    • 1eN = 1.0 × 10N,所以这个值为浮点型
    • 一般我们习惯定义一个int型常数DOM 等于这里的1eN;

2.2 算法原理

2.2.1 状态表示

很显然,这要用到一维的顺序结构,dp表的大小为n + 1

题目要求:

  • 返回到达第n个台阶的爬法数

经验:以某个节点为结尾或者以某个节点为起点去研究问题

  • 此题就是以第i个节点为结尾,用 i 之前的节点推导出第 i 个节点的值
  • 结果应该为dp[n](dp表其中一值)

在这里插入图片描述

得出dp表元素dp[i]的含义就是,到达第 i 个台阶的爬法数

2.2.2 状态转移方程

而得出状态转移方程的思想就是:借用“已知”状态!

  • 我们就幻想dp[i]之前的值,是已知,则有以下操作

我们要到达 i,那么我们到达前的位置可以是:i - 1,i - 2,i - 3:

  1. 到达i - 1,有dp[i - 1]种爬法,然后跳三步到达 i
  2. 到达i - 2,有dp[i - 2]种爬法,然后跳两步到达 i
  3. 到达i - 3,有dp[i - 3]种爬法,然后跳一步到达 i

到达i - 1可以跳两步在跳一步或者跳一步再跳两步之类的啊

  • 没错,但是这包含在情况2和情况3中(到达i -2 或者 到达 i - 3)
    在这里插入图片描述

所以得出dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]

  • 变成泰波那契数列了~

可以通过示例验证一下想法:

在这里插入图片描述

2.2.3 初始化

同样的,i - 3 、i - 2、i - 1 需要考虑数组越界的问题~

2.2.4 填表顺序

dp[i]之前是已知,则填表应从左到右~

2.2.5 返回值

返回dp[n]~

2.3 编写代码

一样的,分为四步:

  1. 创建dp表
  2. 初始化
  3. 填表
  4. 返回值
class Solution {
    public int waysToStep(int n) {
        //1. 创建表
        int[] dp = new int[n + 1];
        int MOD = (int)1e9 + 7; //取模数

        //2. 初始化,处理边界问题
        if(n == 1) {
            return 1;
        }
        if(n == 2) {
            return 2;
        }
        if(n == 3) {
            return 4;
        }
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 4;

        //3. 填表
        for(int i = 4; i < n + 1; i++) {
            dp[i] = ((dp[i - 1] + dp[i - 2]) % MOD + dp[i - 3]) % MOD;
        }

        //4. 返回值
        return dp[n];
    }
}

在这里插入图片描述

  • 没做一次加法就要取模一次!
  • 因为取模满足分配律,所以这样子算得到的结果没问题

在这里插入图片描述

3. 使用最小花费爬楼梯

传送门:力扣746

题目:

在这里插入图片描述

3.1 题目解析

在这里插入图片描述

输入一个cost数组,通过数组得到付费台阶数n,得到终点位置,输出到达终点的最小花费

通过示例探索规律:
在这里插入图片描述

3.2 算法原理

3.2.1 状态表示

很显然,这要用到一维的顺序结构

且dp数组的大小为n + 1

题目要求:返回到达终点的最小花费(理解为到达第n个台阶)

经验:以某一个节点为终点或者以某一个节点为起点去研究

  • 而答案应该对应到dp表中的dp[n](其中一值)
  • 这道题也因此分离出两种解法(先将以某节点为终点的解法

在这里插入图片描述

所以状态表示就是:dp[i]代表从起点到 i 的最小消费

3.2.2 状态转移方程

同样的,把dp[i]之前的值认为“已知”,以此为条件去推导dp[i]

我们要到达 i,那么我们到达前的位置可以是:i - 1,i - 2

  1. 到达i - 1,在原来支付的dp[i - 1]的基础上支付cost[i - 1],跳一步到dp[i]
  2. 到达i - 2,在原来支付的dp[i - 2]的基础上支付cost[i - 2],跳两步到dp[i]

而我们需要取其较小值~

在这里插入图片描述

所以得到,dp[i] = min{dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]}

3.2.3 初始化

对于0和1下标要进行一些初始化~

到达0和1并不需要支付,所以值为0~

3.2.4 填表顺序

由于是以i节点为终点,dp[i]之前的点为条件,所以顺序为左到右

3.2.5 返回值

返回dp[n],其代表第n个台阶(终点)的最小花费~

3.3 编写代码

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int n = cost.length;
        //1. 创建dp表   
        int[] dp = new int[n + 1];
        //2. 初始化,处理边界
        if(n == 0 || n == 1) {
            return 0;
        }
        dp[0] = 0;
        dp[1] = 0;
        //3. 填表
        for(int i = 2; i < n + 1; i++) {
            dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
        }
        //4. 返回值
        return dp[n];
    }
}
  • 直接根据刚才的算法分析写就行了~
    在这里插入图片描述

3.4 解法2:以某节点为起点

在这里插入图片描述

得到状态表示为:dp[i] 代表 第i个台阶跳到终点的最小花费

  • 所以返回值为dp[0] 或者dp[1]的最小值
  • 填表顺序为从右往左

3.4.1 得到状态转移方程

我们认定从dp[i]以后的值为“已知”,利用它们得到结果,从第i个台阶到终点的接下来的一步,要么到达i + 1,要么到达i + 2:

  1. 到达i + 1,支付cost[i]的基础上支付dp[i + 1]后到达终点
  2. 到达i + 2,支付cost[i]的基础上支付dp[i + 2]后到达终点

并且取其较小值最为dp[i]

所以方程为:dp[i] = min{dp[i + 1], dp[i + 2]} + cost[i];

在这里插入图片描述

3.4.2 初始化

dp[n]为终点,值为0

dp[n - 1]到终点只有一步之遥,值为cost[n - 1]

3.4.3 编写代码

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int n = cost.length;
        //1. 创建dp表   
        int[] dp = new int[n + 1];
        //2. 初始化,处理边界
        if(n == 0 || n == 1) {
            return 0;
        }
        dp[n] = 0;
        dp[n - 1] = cost[n - 1];
        //3. 填表
        for(int i = n - 2; i >= 0; i--) {
            dp[i] = Math.min(dp[i + 1], dp[i + 2]) + cost[i];
        }
        //4. 返回值
        return Math.min(dp[0], dp[1]);
    }
}

在这里插入图片描述

所以状态表示的不同,算法思路就有所不同,至于怎么选中呢?

  1. 做题多,经验多
  2. 想到一个,就想尽方法去用这一个去解决问题,解决不了再换(敢于尝试)

4. 解码方法

传送门:力扣91

题目:

在这里插入图片描述

4.1 题目解析

在这里插入图片描述

对于示例,目前看不出什么~
在这里插入图片描述

4.2 算法原理

4.2.1 状态表示

显然,也是一维的dp表(大小为n)

题目要求:输入字符串,返回整个字符串能反解出来的解的个数

经验:以某个节点为终点

  • 那么这个终点只能是字符串的某个字符了~
  • 而答案应该为dp[n - 1](dp表的其中一值)

在这里插入图片描述

那么我们趋向于让答案合理的为dp[n - 1]:

得到状态表示:dp[i]代表字符串[0, i]的子串最多能反解出来的解的个数

4.2.2 状态转移方程

同样的,我们把dp[i]之前的值视为“已知”,把它们当做条件,去推导dp[i]

那么,要扩展到[0, i]的字符串,则扩展成[0, i]字符串之前,应该为[0, i - 1]的子串或者[0, i - 2]的子串:

  1. 如果是[0, i - 1]的子串,则字符串的第i个字符应该单独转化为字母才行,不然前功尽弃!
    • 反解成功:dp[i - 1]
    • 反解失败:0
  2. 如果是[0, i - 2]的子串,则字符串的第i - 1和第i个字符应该成双转化为字母(06之类的不行!)才行,否则前功尽弃!
    • 反解成功:dp[i - 2]
    • 反解失败:0
    • 为什么不可以各自可转化为字母也行?
      • 第i - 1个字符可以单独反解和第i个字符也可以单独反解,此解法在情况1出现过

得出状态转移方程:

dp[i] = (dp[i - 1]) + (dp[i - 2]);

  • 前提是对应项满足条件

4.2.3 初始化

对于0和1下标要特殊处理:

  • 0的话

    1. 如果可以单独成字母 => 1
    2. 不可以 => 0
  • 1的话

    1. 成双结合成字母 => +1
    2. 各自单独成字母 => +1
    3. 都不可以 => 0

4.2.4 填写顺序

由于是以某个节点为终点,所以填写顺序为从左到右~

4.2.5 返回值

返回dp[n - 1],代表整个字符串能反解的解的个数

4.3 编写代码

class Solution {
    public int numDecodings(String s) {
        int n = s.length();

        char[] string = s.toCharArray();        
        //创建dp表
        int[] dp = new int[n];
        //初始化并处理边界问题
        if(string[0] != '0') {
            dp[0] = 1;
        }
        if(n == 1) {
            return dp[0];
        }
        if(string[0] !='0' && string[1] != '0') {
            dp[1]++;
        }
        int number = (string[0] - '0') * 10 + string[1] - '0';
        if(number >= 10 && number <= 26) {
            dp[1]++;
        }
        for(int i = 2; i < n; i++) {
            if(string[i] != '0') {
                dp[i] += dp[i - 1];
            }
            number = (string[i - 1] - '0') * 10 + string[i] - '0';
            if(number >= 10 && number <= 26) {
                dp[i] += dp[i - 2];
            }
        }
        return dp[n - 1];
    }
}

在这里插入图片描述

在这里插入图片描述

  • 总体代码跟刚才的原理对应,理解起来也没啥问题
  • 但是明显的,这个初始化未免也太长了吧~

而且,跟填表操作还差不多:

  • 写得很别扭~
    在这里插入图片描述

4.4 代码简化技巧

  • 这就衍生出一种做法,就是在添加一个“假的数据“,而这个假的数据仍让状态转移方程成立~
  • 这样就可以将重复的代码放在一起了~
    在这里插入图片描述

而这个fake为多少呢,一般是为0的,但是一定要据实际而言!

  • 这道题fake为1才对,因为string[1]和string[2]组成功后,是一种才对,所以dp[0]=1

所以新dp表的大小为n + 1,返回值为dp[n];

注意这里的dp[i]代表的是[0, i)的字符串子串!

class Solution {
    public int numDecodings(String s) {
        int n = s.length();

        char[] string = s.toCharArray();        
        int[] dp = new int[n + 1];
        dp[0] = 1;
        if(string[0] != '0') {
            dp[1] += 1;
        }
        if(n == 1) {
            return dp[1];
        }
        for(int i = 2; i < n + 1; i++) {
            if(string[i - 1] != '0') {
                dp[i] += dp[i - 1];
            }
            int number = (string[i - 2] - '0') * 10 + string[i - 1] - '0';
            if(number >= 10 && number <= 26) {
                dp[i] += dp[i - 2];
            }
        }
        return dp[n];
    }
}

在这里插入图片描述

这个小优化能掌握是更好的~


文章到此结束!谢谢观看
可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭🦆

动态规划第一章结束啦,本章节讲解的类型都差不多,是不是有点“斐波那契”那味!已知推未知!牢记思想流程,特别重要

如果你理解的不错,那你很有天赋!

这动态规划的第一步走的不错!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/643933.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决Centos安装时找不到磁盘:未选择任何磁盘(no diks selected),本地标准磁盘为空

文章目录 问题描述问题原因解决办法 问题描述 笔者最近又买了一台新电脑&#xff0c;并打算在上面安装 Linux 来充当一个新的服务器结点。但很不幸的是&#xff0c;每次笔者略微尝试新事物时&#xff0c;都要踩很多坑。笔者在使用 U 盘刻录 CentOS 8 镜像之后&#xff0c;准备在…

公平锁/非公平锁/可重入锁/自旋锁

在JAVA中我们知道有很多加锁的方式&#xff0c;比如常见的 通过synchronized关键字&#xff0c;还有Lock&#xff0c;还有之前说原子CAS操作时有看到过的死循环方式的自旋锁。 借此来说一下锁的分类: 公平锁: 是指多个线程按照申请的顺序来获取锁&#xff0c;每次获取锁时会…

dp算法篇Day1

"多希望有人来陪我&#xff0c;度过末日啊~" 讲讲我为什么突然想更新这篇栏目。 想想自己也算 "系统" 接触计算机这个学科也有差不多一年了&#xff0c;回想起当初下定决心要全身心投入到这个专业或者说行业中来&#xff0c;现在到了这样的地步&#xff0c…

CSS基础学习--10 margin(外边距)

一、定义&#xff1a; CSS margin(外边距)属性定义元素周围的空间。 二、margin margin 清除周围的&#xff08;外边框&#xff09;元素区域。margin 没有背景颜色&#xff0c;是完全透明的。 margin 可以单独改变元素的上&#xff0c;下&#xff0c;左&#xff0c;右边距&a…

今天面了个35k字节跳动出来,真是砂纸擦屁股,给我露了一手...

​2023年春招已经结束&#xff0c;很多小伙伴收获不错&#xff0c;拿到了心仪的 offer。 各大论坛和社区里也看见不少小伙伴慷慨地分享了常见的面试题和八股文&#xff0c;为此咱这里也统一做一次大整理和大归类&#xff0c;这也算是划重点了。 俗话说得好&#xff0c;他山之石…

AI 绘画(0):导论

文章目录 导论感谢人员Ai绘画前期准备软件环境硬件条件 Ai绘画介绍Ai绘画简单流程介绍Ai绘画软件介绍参数输入介绍 Ai绘画公约 导论 Ai绘画是最近比较热门的绘画方式&#xff0c;以干掉原画师为口号&#xff0c;引起了激烈的讨论。Ai绘画能否取代人工我们先不谈&#xff0c;但…

一个专科生的 Python 转行之路

自学之路 正式开始学编程是在十月底的样子, 那时候在知乎 flask 话题下看到一个问题 「有多少人按萧井陌大神给出的PythonFlask路线找到工作了&#xff1f;」。就觉得自己也可以啊, 就开始辞职自学 Python,。 刚开始的那一个月确实能够做到每天 10 个小时写代码, 学了大概一个…

java设计模式之:外观模式

前言 举个现实生活中例子&#xff0c;泡茶和去茶馆喝茶的区别&#xff0c;如果是自己泡茶需要自行准备茶叶、茶具和开水&#xff0c;而去茶馆喝茶&#xff0c;最简单的方式就是跟茶馆服务员说想要一杯什么样的茶&#xff0c;是铁观音、碧螺春还是西湖龙井&#xff1f;正因为茶…

互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景

多线程访问共享资源的时候&#xff0c;避免不了资源竞争而导致数据错乱的问题&#xff0c;所以我们通常为了解决这一问题&#xff0c;都会在访问共享资源之前加锁。 最常用的就是互斥锁&#xff0c;当然还有很多种不同的锁&#xff0c;比如自旋锁、读写锁、乐观锁等&#xff0…

数据结构——树和二叉树

文章目录 **一 数的基本概念****1 定义****2 基本术语****3 树的性质** **二 二叉树的概念****1 二叉树的定义和特性****1.1 定义****1.2 特殊的二叉树****1.3 二叉树的性质** **2 二叉树的存储结构****2.1 顺序存储结构****2.2 链式存储结构** **三 二叉树的遍历和线索二叉树*…

Spark SQL数据源的基本操作(更新ing)

文章目录 一、基本操作二、默认数据源&#xff08;一&#xff09;默认数据源Parquet&#xff08;二&#xff09;案例演示读取Parquet文件1、在Spark Shell中演示练习1、将student.txt文件转换成student.parquet练习2、读取student.parquet文件得到学生数据帧&#xff0c;并显示…

K8S minikube本地安装

一. mac安装K8S 1.brew安装 brew install kubectl 2.查看版本 kubectl version --outputjson { "clientVersion": { "major": "1", "minor": "27", "gitVersion": "v1.27.2", &…

基于深度学习的高精度奶牛检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度奶牛检测识别系统可用于日常生活中或野外来检测与定位奶牛目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的奶牛目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检测模型…

locked勒索病毒利用零日漏洞,企业服务器数据瞬间遭受致命加密

目录 引言&#xff1a; 事件概述&#xff1a; .locked勒索病毒加密算法&#xff1a; 数据恢复建议&#xff1a; locked勒索病毒数据恢复案例&#xff1a; 什么叫零日漏洞&#xff1f; 对策建议&#xff1a; 引言&#xff1a; 近日&#xff0c;网络安全界再次爆发了一起…

RK3588平台开发系列讲解(系统篇)开机启动原因

文章目录 一、系统开机启动原因二、开机启动场景沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要讲解平台系统开机启动原因介绍。 一、系统开机启动原因 开机原因记录文件在/proc/sys/kernel/boot_reason,那么开机后可以从这个文件中读取数值来获知本次开机…

锁升级:无锁、偏向锁、轻量级锁、重量级锁

锁升级 JDK 1.6之前&#xff0c;synchronized 还是一个重量级锁&#xff0c;是一个效率比较低下的锁。但是在JDK 1.6后&#xff0c;JVM为了提高锁的获取与释放效率对synchronized 进行了优化&#xff0c;引入了偏向锁和轻量级锁 &#xff0c;从此以后锁的状态就有了四种&#…

开源SCRM营销平台MarketGo-数据管理

一、概述 企业在私域运营的场景下&#xff0c;系统在运行中会产生一些用户数据和行为数据。 用户数据包含年龄&#xff0c;性别&#xff0c;生日&#xff0c;电话&#xff0c;用户标签&#xff0c;还有用户和员工的关系等信息。行为数据包含在SCRM中创建活动的用户事件&#…

自学黑客(网络安全)?一般人我劝你还是算了吧!

前言 博主本人 18年就读于一所普通的本科学校&#xff0c;21年 6 月在三年经验的时候顺利通过校招实习面试进入大厂&#xff0c;现就职于某大厂安全联合实验室。 我为啥说自学黑客&#xff08;网络安全&#xff09;&#xff0c;一般人我还是劝你算了吧。因为我就是那个不一般的…

【C++】c++11的新特性——右值引用/移动语义/lambda表达式

文章目录 C11介绍1. 统一的列表初始化1.1 {}初始化1.2 std::initializer_list 2. 一些关键字2.1 auto2.2 decltype2.3 nullptr 3. 范围for4. 右值引用和移动语义&#xff08;重点&#xff09;4.1 左值引用和右值引用4.2 右值引用的应用4.3 总结 5. 万能引用和完美转发6. 新的类…

mysql小表驱动大表

摘要&#xff1a; 小表驱动大表为了减少匹配时的数据量 判断谁做小表时&#xff0c;是比较算上过滤条件后的数量 left join时手动设置小表驱动大表 inner join时优化器会自动小表驱动大表 course–100条数据 student_info–100w条数据 优化器会选择小表驱动大表&#xff08;这里…