2024年Java最新轻松学DP——动态规划 + 0-1背包,java工程师面试突击第二季分布式

总结

面试难免让人焦虑不安。经历过的人都懂的。但是如果你提前预测面试官要问你的问题并想出得体的回答方式,就会容易很多。

此外,都说“面试造火箭,工作拧螺丝”,那对于准备面试的朋友,你只需懂一个字:刷!

给我刷刷刷刷,使劲儿刷刷刷刷刷!今天既是来谈面试的,那就必须得来整点面试真题,这不花了我整28天,做了份“Java一线大厂高岗面试题解析合集:JAVA基础-中级-高级面试+SSM框架+分布式+性能调优+微服务+并发编程+网络+设计模式+数据结构与算法等”

image

且除了单纯的刷题,也得需准备一本【JAVA进阶核心知识手册】:JVM、JAVA集合、JAVA多线程并发、JAVA基础、Spring 原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB、Cassandra、设计模式、负载均衡、数据库、一致性算法、JAVA算法、数据结构、加密算法、分布式缓存、Hadoop、Spark、Storm、YARN、机器学习、云计算,用来查漏补缺最好不过。

image

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

/*

题目描述

给定一个字符串s和一个词典wordDict,确定s是否可以根据词典中的词分成一个或多个单词。

比如,

给定 s = "leetcode"

wordDict = ["leet", "code"]

返回true,因为"leetcode"可以被分成"leet code"

*/

public class LeetCode139_单词拆分 {

/*

状态:

    子状态:前1,2,3,...,n个字符能否根据词典中的词被成功分词

    F(i): 前i个字符能否根据词典中的词被成功分词

状态递推:

    F(i): true{j <i && F(j) && substr[j+1,i]能在词典中找到} OR false

    在j小于i中,只要能找到一个F(j)为true,并且从j+1到i之间的字符能在词典中找到,

    则F(i)为true

初始值:

    对于初始值无法确定的,可以引入一个不代表实际意义的空状态,作为状态的起始

    空状态的值需要保证状态递推可以正确且顺利的进行,到底取什么值可以通过简单

    的例子进行验证

    F(0) = true

返回结果:F(n)

*/



//注意边界值问题,因为在字符串中存储是从0下标开始,而在数组中实际是从1开始、arr[0]为辅助--用来判断

public boolean wordBreak(String s, List<String> dict) {

    boolean[] canBreak = new boolean[s.length() + 1];

    // 初始化F(0)=true

    canBreak[0] = true;



    for (int i = 1; i <= s.length(); i++) {

        // true{j < i && F(j) && substr[j+1,i]能在词典中找到}

        for (int j = 0; j < i; j++) {

            // F(j): 之前的字符串已经确定了可以有从集合中找到分割串的结果为true

            // 如果从[j+1, i]这个子串也在集合中,那么相对的 F(i) 就找到了结果,为true

            if (canBreak[j] && dict.contains(s.substring(j, i))) { //substring左闭右开且字符向前挪一个

                // 通过前面已知的 F(j) 从小到大来求 F(i)

                canBreak[i] = true;

                break;

            }

        }

    }

    return canBreak[s.length()];

}

}




### []( )[LeetCode120\_三角形最小路径和]( )



![在这里插入图片描述](https://img-blog.csdnimg.cn/ce52fc86edd647919407a5487d672d26.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6Zi_5biD772e,size_20,color_FFFFFF,t_70,g_se,x_16)  

这个问题我们来用两种方法递推:自顶向下、自底向上



开始喽~



三角形是数组形象化的表现,我们要做的是,



自顶向下:



从三角形的顶点走到底部,根据:`每一步只能移动到下一行的相邻节点(节点下标相同或者节点下标+1)`这个约束,走到底部的时候,找出一条路径最小的加和,返回这个最小和数。



这个问题的子问题要怎么来考虑的,即就是:我们的三角形有 n 层,最终也就是要从第 n 层这一层中找一个位置,从他走下来这条路径为加和最小值,全局最优解。那我们的局部最优解就可以往上那个看呀~ 第 n-1层,n-2层…第2层,当然,第 1 层只有一个数,这个数是必须要包括的。



分析完思想,我们来说做法: 定义一个和原数组大小相同的二维数组,一层一层填充这个数组,从上往下,一层一层找路走,把以上的加和放在这个位置,



状态: `F(i,j) = Math.min( F(i-1, j-1), F(i-1, j)) + triangle[i][j]`



最后就从最后一层中返回一个位置为最小和的结果就好啦~



点个赞呗~



代码:



import java.util.ArrayList;

import java.util.List;

// 问题:从顶部到底部的最小路径和

// 状态定义:从(0,0)到(i,j)的最小路径和

// 状态转移方程:F(i,j):Math.min(F(i-1,j),F(i-1,j-1)) + array[i][j]

// (j == 0 || j == i) : F(i,j)

// j == 0: F(i-1,0) + array[i][0]

// j == i: F(i-1,j-1) + array[i][j]

// 状态初始化:F(0,0) = array[0][0]

// 返回结果:Math.min(F(row-1,j)…)

public class LeetCode120_三角形最小路径和 {

public int minimumTotal(List<List<Integer>> triangle) {

    if(triangle.size() == 0 ) {

        return 0;

    }

    List<List<Integer>> minPathSum = new ArrayList<>();

    for (int i = 0; i < triangle.size(); i++) {

        minPathSum.add(new ArrayList<>());

    }

    // F[0][0]初始化

    minPathSum.get(0).add(triangle.get(0).get(0));

    for (int i = 1; i < triangle.size(); i++) {

        int curSum = 0;

        for (int j = 0; j <= i; j++) {

            if (j == 0){

                curSum = minPathSum.get(i-1).get(0);

            }else if (j == i) {

                curSum = minPathSum.get(i-1).get(j-1);

            }else {

                curSum = Math.min(minPathSum.get(i-1).get(j-1),

                        minPathSum.get(i-1).get(j));

            }

            minPathSum.get(i).add(triangle.get(i).get(j) + curSum);

        }

    }

    int size = triangle.size();

    int allMin = minPathSum.get(size-1).get(0);

    for (int i = 1; i < size; i++) {

        allMin = Math.min(allMin,minPathSum.get(size-1).get(i));

    }

    return allMin;

}

}




自底向上思想:这个更容易理解,看下面的注解就懂啦



// 自底向上

// 状态F(i,j): 从(i,j)到达最后一行的最小路径和

// 状态转移方程:

//      F(i,j): min(F(i+1,j),F(i+1, j+1)) + array[i][j]

// 初始状态:

//      F(row - 1) = array[row-1][j]

// 返回结果:

//      F(0,0)

public int minimumTotal2(int[][] triangle) {

    int row = triangle.length;

    for (int i = row-2; i >= 0 ; i--) {

        for (int j = 0; j <= i; j++) {

            triangle[i][j] = Math.min(triangle[i+1][j+1],triangle[i+1][j]) + triangle[i][j];

        }

    }

    return triangle[0][0];

}



> 来两道练习题巩固一下吧:  

> [LeetCode62\_不同路径]( )

> 

> [LeetCode64\_最小路径和]( )



[]( )0-1 背包问题

===========================================================================



背包问题可以通过动态规划算法来实现。



[]( )什么叫01背包问题?

-----------------------------------------------------------------------------



> 背包问题通俗的说,就是假如你⾯前有3块宝⽯分别为a, b, c, 每块宝石的重量不同,并且每块宝石所带来的价值也不同(注意:这里宝石的重量的价值没有特定关系),目前我们有⼀个背包,只有固定的容量,要解决的问题就是在⼀定容量的背包⾯前装哪几块宝⽯才能获取到最大的价值,对于每块宝石我们只有拿或者不拿这两种选择,拿为1不拿为0,因此叫做0-1背包问题。



[]( )示例

---------------------------------------------------------------------



有一个背包,其容量为 4 ,有三块宝石a,b,c,大小分别为:\[1, 4, 3\];价值分别为:\[15, 30, 20\],怎么装才能使得获取的价值最大?



| 宝石 | 重量 | 价值 |

| --- | --- | --- |

| A | 1 | 15 |

| B | 4 | 30 |

| C | 3 | 20 |



⾸先对于我们人去操作而言,首先考虑应该是质量最轻,并且价值最大的,从数据中我们可以看到宝石c质量最小,且价值最大,应该优先考虑装这⼀块,然后依次考虑其他的。这种方式就是考虑性价比最高的宝⽯。我们可以将这个问题进⾏简化,目前是背包承重为4,因此,我们的选择较多,不知从何下⼿,那么我们假设背包的承重为3或者是2甚至是 1。在这种情况下,我们的选择就不多了,对于人类选择也是,在选择不多的情况下更容易找出最优方案,同样计算机也是。因此我们的背包问题也是从这开始,**将选择较多的问题转化为选择不多的问题。在选择不多的情况下进行选择,有利于比较判断,依次递增,** 最后解决背包承重为11的问题。



> 我们知道:对于背包问题,其实是⼀个动态规划,对于动态规划,⼤多数都是⽤⼀个数组来进⾏保存。那么我们来试着填充一下这个数组。



| 宝石 | 0 | 重量1的价值 | 重量2的价值 | 重量3的价值 | 重量4的价值 |

| --- | --- | --- | --- | --- | --- |

|  | 0 | 0 | 0 | 0 | 0 |

| A | 0 | 15(a) | 15(a) | 15(a) | 15(a) |

| B | 0 | 15(a) | 15(a) | 15(a) | 30(取a放b) |

| C | 0 | 15(a) | 15(a) | 20(取a放c) | **35(a+c)** |



**说明:**  

i:行坐标,代表宝石  

j:列坐标,代表背包容量大小  

v\[i\]\[j\]:前 i 个宝石中能够装入容量为 j 的背包里的最大价值。



`装入顺序从上到下、从左到右`



### []( )填表过程:



1.  v\[i\]\[0\]:当容量为 0 时,价值为 0,所以这一列都为 0。

2.  v\[0\]\[j\]:不放宝石,价值为0。

3.  接下来一行一行看,对于宝石a,他在容量为1,2,3,4的情况下,都可以放入,我们在第一行表中填入宝石a的价值。

4.  再看宝石b,因为它的重量为4,所以在容量为1,2,3,的情况下它是放不下的,所以我们直接把上一行的价值拿下来,也就是此时包里装的是宝石a的价值。当容量为 4 时,可以放得下宝石b 了,但是因为包包里面此时有宝石a 占着空间,此时我们比较 \[宝石a 的价值 < 宝石b 的价值\] 就理所当然的取出a ,放入b,使价值最大。

5.  最后我们放入宝石c,因为它的重量为3,所以在容量为1,2,的情况下它是放不下的,所以我们直接把上一行的价值拿下来,也就是此时包里装的是宝石a的价值。当容量为 3 时,可以放得下宝石b 了,但是因为包包里面此时有宝石a 占着空间,此时我们比较 \[宝石a 的价值 < 宝石c 的价值\] 就理所当然的取出a ,放入c,使价值最大,到容量为 4 时,背包此时还有剩余的空间大小为 1,它可以容纳宝石a,这时自然而然把宝石a 放入,此时背包内的总价值就为 35,也是最优解。



[]( )背包问题的思路:

---------------------------------------------------------------------------



> 背包问题主要是指⼀个给定容量的背包、若干具有⼀定价值和重量的物品,如何选择物品放入背包使物品的价值最大。 这里的0-1背包是每个物品只能放一个,0→不放,1→放,求最大价值。



**利用动态规划来解决。每次遍历到的第i个物品,根据w\[i\]和v\[i\]来确定是否需要将该物品放⼊背包中。即对于给定的n个物品,设v\[i\]、w\[i\]  



# 最后

金三银四马上就到了,希望大家能好好学习一下这些技术点

学习视频:

![](https://img-blog.csdnimg.cn/img_convert/b40ed2c9307eef74ceffc27d01ef7379.webp?x-oss-process=image/format,png)

大厂面试真题:

![](https://img-blog.csdnimg.cn/img_convert/6dd57d72f2f4dbf70264b746ec281f8e.webp?x-oss-process=image/format,png)

> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)收录**

**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

解决。每次遍历到的第i个物品,根据w\[i\]和v\[i\]来确定是否需要将该物品放⼊背包中。即对于给定的n个物品,设v\[i\]、w\[i\]  



# 最后

金三银四马上就到了,希望大家能好好学习一下这些技术点

学习视频:

[外链图片转存中...(img-xQpXigmF-1714992037659)]

大厂面试真题:

[外链图片转存中...(img-U8xIqV4r-1714992037659)]

> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)收录**

**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值