前言
【校招常见算法】暴力法、模拟,是最基础的算法,我们后面要学习的算法很多时候都要先从暴力法、模拟开始去思考,最后才能想出比较优的解法。
视频链接:【校招常见算法】暴力法、模拟
什么是暴力法?
第一次认识到 暴力法还是在大一的CPP课本例题鸡兔同笼问题。今有雉兔同笼,上有三十五头,下有九十四足,问雉兔各几何?翻译过来就是:有若干只鸡兔同在一个笼子里,从上面数,有35个头,从下面数,有94只脚,问笼中各有多少只鸡和兔?
小学数学里用解方程的方式可以解出来,用暴力法也可以把解决这个问题:鸡最少可以有0只,最多可以有35只(因为有35个头),兔子最少可以有0只,最多有23只(因为有94只脚)。
把鸡和兔子所有的组合列出来,组合有很多:[0,0]、[0,1]、[0,2]...[35,23]。把各种组合的头和脚加起来,如果是符合题目要求的,说明就是答案,代码如下:
for(int x=0;x<=35;x++) {//列出所有鸡的数量
for(int y=0;y<=23;y++) {//列出所有兔子的数量
if(x+y==35 && 2*x+4*y==96) {//判断头和脚的数量符合要求
System.out.println("鸡有"+x+"只,兔子有"+y+"只");
}
}
}
所谓暴力法,
就是把所有情况不重复、不遗漏地枚举出来,然后找到我们想要的答案。枚举就是列举的意思,注意这里的
不重复和
不遗漏,如果遗漏的话,就可能会把我们要的答案也漏掉了,至于不重复,其实有的题目如果重复了也没有影响,能不能重复具体得看题目。
什么是模拟?
暴力模拟,往往我们是把它们连在一起说的, 模拟就是没有使用什么特别复杂的算法直接按照题目意思去做就可以了,注意各种细节和边界条件。一般来说模拟没什么难度,但要注意各种细节和边界条件了。
理解了它们的概念后,上题目。
例题
1、leetcode:1. 两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
提示:
- 2 <= nums.length <= 10^3
- 10^9 <= nums[i] <= 10^9
- 10^9 <= target <= 10^9
- 只会存在一个有效答案
★题意:
在一个数组里找两个数字,使得它们的和等于target,这里提到了只会有一个有效答案,所以就不用考虑多个答案的情况了。
🎈思路:
这道题目有很多种做法,我们这里用暴力法去解决这道题目,我们可以枚举出所有的所有的两数组合,然后再去找到和为target的组合。
例如样例[1,2,3,4],它的组合有[1,2 ]、[1,3]、[1,4]、[2,3]、[2,4]、[3,4]。
所有的数字都可能是第一个数字,也可能是第二个数字,可以直接用两层循环去枚举。第一个数字在前面,用一个循环枚举;第二个数字在后面,在循环里再嵌套一个循环去枚举。为了避免重复,在枚举的时候第二个数字必须在第一个数字的后面。
✍参考代码:
public int[] twoSum(int[] nums, int target) {
int[] ans=new int[2];
//i枚举第一个数字,j枚举第二个数字,如果相加等于target,就放ans中
for(int i=0;i<nums.length;i++) {
for(int j=i+1;j<nums.length;j++) {
if(nums[i]+nums[j]==target) {
ans[0]=i;
ans[1]=j;
}
}
}
return ans;
}
2、leetcode:11. 盛最多水的容器
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点(i,ai) 。在坐标内画 n 条垂直线,垂直线 i的两个端点分别为(i,ai)和(i, 0)。找出其中的两条线,使得它们与x轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1]
输出:1
示例 3:
输入:height = [4,3,2,1,4]
输出:16
示例 4:
输入:height = [1,2,1]
输出:2
提示:
- n = height.length
- 2 <= n <= 3 * 10^4
- 0 <= height[i] <= 3 * 10^4
★题意:
相当于给我们多块木板的高度,木板固定在下标中,挑出两块板子构成容器,问我们最多可以装多少水。
🎈思路:
leetcode上很经典的题目,这里用暴力法做,和两数之和差不多,我们把所有的容器体积都列出来,然后找一个最大的体积。
比如样例[2,1,3,4],一共有6种容器分别是[2,1]、[2,1,3]、[2,1,3,4]、[1,3]、[1,3,4]、[3,4]。
所有的木板都可以是最左边的木板和最右边的木板,但最右的木板在最左的木板右边。直接用两层嵌套循环枚举所有容器,然后取最大值。
✍参考代码:
public int maxArea(int[] height) {
int ans=0;
//i枚举最左边的木板,j枚举最右边的木板,得到当前容器的体积和ans取最大
for(int i=0;i<height.length;i++) {
for(int j=i+1;j<height.length;j++) {
ans=Math.max(ans, (j-i)*Math.min(height[i], height[j]));
}
}
return ans;
}
3、leetcode:67. 二进制求和
给你两个二进制字符串,返回它们的和(用二进制表示)。
输入为 非空字符串且只包含数字1和0。
示例1:
输入: a = "11", b = "1"
输出: "100"
示例2:
输入: a = "1010", b = "1011"
输出: "10101"
提示:
- 每个字符串仅由字符 ‘0’ 或 ‘1’ 组成。
- 1<=a.length, b.length<=10^4。
- 字符串如果不是 “0” ,就都不含前导零。
🎈思路:
像十进制加法一样去模拟二进制的加法,从最后一位数字对齐往前摆竖式进行计算,把逢十进一的加法规则换成逢二进一就可以了。但是有好多的细节要注意,这里用一个十进制加法来举例。
⏳细节:
1、为了方便处理数字对不齐的情况,我们可以在短的数字前面补0。
2、每次计算需要加上进位,更新进位,加上进位,进位是计算后和的十位,答案是计算后和的个位数。
3、计算完最好位之后可能会有进位溢出,需要把最后的进位加上去。
✍参考代码:
public String addBinary(String a, String b) {
//给长度比较短的字符串往前面补0
while(a.length()<b.length())
a="0"+a;
while(b.length()<a.length())
b="0"+b;
int cf=0;//进位标记
String ans="";
//二进制运算从后往前加,同时加上进位,放到ans中并更新进位
for(int i=b.length()-1;i>=0;i--) {
int s=a.charAt(i)-'0'+b.charAt(i)-'0'+cf;
ans=(s%2)+ans;//更新答案
cf=s/2;//更新进位
}
//如果最后进位不为零,要加上最后一位
if(cf>0)
ans=cf+ans;
return ans;
}
小结
暴力法要注意 不重复、 不遗漏,才能避免对正确答案造成影响。模拟需要注意 边界和 细节条件。遇到复杂的模拟尽可能地 简化代码,减少代码出错的概率,就像二进制求和,需要注意处理数字对不齐、更新进位、进位溢出。暴力法、模拟这期就先到这里,下期再见。
没有人,想在年少的时候成为一个普通人。我是可乐,力争将枯燥难懂的知识带给大家,感谢你的观看。