leetCode进阶算法题+解析(二)
字符串转换整数
题目:请你来实现一个 atoi 函数,使其能将字符串转换成整数。首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。在任何情况下,若函数不能进行有效的转换时,请返回 0。说明:假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [?231, 231 ? 1]。如果数值超过这个范围,请返回 INT_MAX (231 ? 1) 或 INT_MIN (?231) 。
示例 1:思路:这个题的叙述文字很多啊,多的我都觉得是不是我看漏什么了。单纯的说这道题感觉应该不难,首先第一个字符是字母则返回0.第一个字符是+-则用一个boolean判断这个数是正是负。然后接下来的数字超过整数最大值则返回int最小值。差不多就是这个逻辑,我去实现看看。
输入: "42"
输出: 42
示例 2:
输入: " -42"
输出: -42
解释: 第一个非空白字符为 '-', 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:
输入: "4193 with words"
输出: 4193
解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。
示例 4:
输入: "words and 987"
输出: 0
解释: 第一个非空字符是 'w', 但它不是数字或正、负号。
因此无法执行有效的转换。
示例 5:
输入: "-91283472332"
输出: -2147483648
解释: 数字 "-91283472332" 超过 32 位有符号整数范围。
因此返回 INT_MIN (?231) 。
面向测试案例编程。。。一次次尝试补漏,一点点改动终于是改完了,性能超过了百分之九十九。我直接贴代码:
class Solution {
public int myAtoi(String str) {
long res = 0;
boolean flag = true;
int f = -1;
char[] c = str.toCharArray();
for(int i = 0;
iInteger.MAX_VALUE){
if(!flag) returnInteger.MIN_VALUE;
return Integer.MAX_VALUE;
}
f = 1;
}else if(c[i]>30 ){
if(f==-1 && c[i]==' ') continue;
break;
}
}
return flag == true?(int)res:(int)-res;
}
}
首先这个一开始我想到可能溢出所以直接用long保存的,但是万万没想到long也能溢出,所以改成了每次追加都要判断,int溢出了直接返回。
其次测试案例+-1这种,一开始我也没想到,所以在没数字之前的+-正常判断的,所以添加了f表示第一个正负号已经搞定。反正都是小问题,一点点补漏就行了。感觉这道题对不起中等难度,下一题了。
盛最多水的容器
题目:给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。说明:你不能倾斜容器,且 n 的值至少为 2。
文章图片
题目截图 思路:暂时没啥思路啊,就是生算呗。。。我打算先暴力法试试水。就是第一个跟后面每一个试试,第二个跟后面每一个试试,,以此类推,不超时肯定是稳了,,超时再说超时的。我去撸代码了。
咳咳,喜大普奔,暴力法通过了, 但是遗憾的是将将通过,性能排行二十多。。我先贴上代码,再想想不暴力要怎么做:
class Solution {
public int maxArea(int[] height) {
int res = 0;
for(int i = 0;
i
其实换个思路,不用全遍历,因为在给定值后面的,最高值前面的那些都不用计算,毕竟没最高值长,也没最高值高,不可能比最高值大,但是要怎么写呢?还有当前值*最后值的长度也没当前面积大的页不用考虑了。其实优化点还很多,我一点点看看怎么写。
改了一点点,但是还没到达想要的目标。 尽我所能一点点调试才到百分之三十八。估计不换思路也就这样了,附上改良版代码:
class Solution {
public int maxArea(int[] height) {
int res = 0;
int max = height[0];
int len = height.length;
for(int i = 0;
i
文章图片
性能改良过程 我还是直接看性能排行第一的代码吧。
好了,看完了并且分析完了,而且自己用自己的方式写完了,先说一下大概思路:双指针,首先第一步第一个和最后一个的面积。然后两个指针一点点往中间走,如果遇到比边上的还要短的直接继续往下,因为长度已经在缩短了,如果长度还要短就没啥可比性了,接下来贴代码:
class Solution {
public int maxArea(int[] height) {
int res = 0;
int l = 0;
int r = height.length-1;
while(lheight[l] && lheight[r] && r>=0){
r--;
}
}
}
return res;
}
}
其实这个厉害的是思路,我在做的时候就若有若无的有点小思路,但是没有坚持完善成代码。急功近利的锅,虽然有可能坚持也想不到这样。反正又学到了一招。
感觉现在刷题每道题都能学到东西,挺好的。下一题吧。
整数转罗马数字
题目:罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符数值
I1
V5
X10
L50
C100
D500
M1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。
示例 1:思路:这个题做过类似的,我记得当时是罗马数字转整数,是个简单的题目。这个就是反过来,我记得麻烦是很麻烦,但是并不难。因为题目范围是1-3999之间,我完全可以钻空子,(顺便说一下,前几天做的两个非0数和的那个也是钻了这个空子,其实我自己感觉也不太好,毕竟取值范围扩大了就逻辑啥的都要重新写,但是不得不说好用啊~)直接判断数的大小然后去写。我去实现啦。
输入: 3
输出: "III"
示例 2:
输入: 4
输出: "IV"
示例 3:
输入: 9
输出: "IX"
示例 4:
输入: 58
输出: "LVIII"
解释: L = 50, V = 5, III = 3.
示例 5:
输入: 1994
输出: "MCMXCIV"
解释: M = 1000, CM = 900, XC = 90, IV = 4.
好的,今天的第一道一次及格的题目。就是生生写了好几十行判断的,我直接贴出来:
class Solution {
public String intToRoman(int num) {
StringBuffer sb = new StringBuffer();
while(num>=1000){
sb.append("M");
num -= 1000;
}
if(num>=900){
sb.append("CM");
num -= 900;
}
if(num>=500){
sb.append("D");
num -= 500;
}
if(num>=400){
sb.append("CD");
num -= 400;
}
while(num>=100){
sb.append("C");
num -= 100;
}
if(num>=90){
sb.append("XC");
num -= 90;
}
if(num>=50){
sb.append("L");
num -= 50;
}
if(num>=40){
sb.append("XL");
num -= 40;
}
while(num>=10){
sb.append("X");
num -= 10;
}
if(num>=9){
sb.append("IX");
num -= 9;
}
if(num>=5){
sb.append("V");
num -= 5;
}
if(num>=4){
sb.append("IV");
num -= 4;
}
while(num>=1){
sb.append("I");
num -= 1;
}
return sb.toString();
}
}
然后挺开心的,而且我看了下,已经给定的罗马字也没啥数组扩大的情况。比如超过5千题目上也没有啊。而且改动只要在最开始添加几个判断就行了,反正对自己挺满意。
接下来去看看排行第一的代码:
看了排行第一的代码,其实就是把我这个整合到一个循环里,而且用了两个数组来让数字和值对应上,不难,挺简单的,我贴出来分享下然后下一题了:
class Solution {
public String intToRoman(int num) {
int[] nums = {1,4,5,9,10,40,50,90,100,400,500,900,1000};
String[] roma = {"I","IV","V","IX","X","XL","L","XC","C","CD","D","CM","M"};
StringBuilder sb = new StringBuilder();
int i = nums.length-1;
while(i>=0) {
if (num >=nums[i]) {
sb.append(roma[i]);
num = num-nums[i];
}
else i--;
}
return sb.toString();
}
}
三数之和
题目:给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。注意:答案中不可以包含重复的三元组。
示例:思路:目前刚刚审完题,思路是三次循环,第一层i=0开始,第二层j=i+1开始,第三层k=j+1开始,如果i+j+k等于0就add进结果集。暂时就这个想法,不知道超时不,我去实现了。
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
感觉三重循环这个死在了不能有重复的三元组,所以换个思路,还是别暴力法了,想想什么技巧可言解决这个问题。
额, 我又想到了数组下标代替数字。因为这个是有正有负的,所以目前打算是两个数组,一个正一个负。然后负的两个相加如果正的这个值是有的,则是一对。反之没有继续往下。毕竟abc相加等于0除了三个000必然有正有负。就是不知道这个题abc的范围是多少。但是我觉得思路是没问题的,我去试试。
很好,又超时了!!!最后的数字大于数百万。。313个测试案例过了311个,最后两个卡主了。我也很无奈啊。。继续想怎么处理吧。
或者放在set中,判断是否有这个值?然后重复的元素单独处理、我再去试试。
好的吧,这个题我绝望了,做了一下午两个多小时,超时两次,剩下各种bug,直接看了题解的思路,居然回归到一开始的循环,只不过处理的优雅了点,变成了两层循环,并且用sort排序处理重复的三元组。。我去试着根据理解写代码了。
写是写出来了,然后性能依旧可怜,我不知道是不是因为我自己写list的原因,一会再调整,我先贴代码:
class Solution {
public List> threeSum(int[] nums) {
List> res = new ArrayList>();
Arrays.sort(nums);
//当第i个元素大于0说明已经不可能凑成三元组了
//所以这里一定是主动break,无所谓i的范围
for(int i = 0;
i0) break;
//第一个正常算,第二个开始直接continue
if(i>0 && nums[i]==nums[i-1]) continue;
int l = i+1;
//第二个元素
int r = nums.length-1;
//第三个元素
//这里用了双指针,不断往中间计算,省去了两次循环
while(ll && nums[r]==nums[r-1]) r--;
//注意这里是除了while还要加加减减,因为不相等的时候都不进while
l++;
r--;
}else if (sum>0) {//大于0说明右边的大了,往左移指针
r--;
}else {//小于0说明左边的小了,往右移指针
l++;
}
}
}
return res;
}
public List getList(int i,int j,int k){
List list = new ArrayList();
list.add(i);
list.add(j);
list.add(k);
return list;
}
}
因为这个题我思路也不是很清楚,所以都是一点点写代码的。感觉写的挺全了。接下来我去试着优化下了。
把这个新添加list改成数组转list的形式,时间超过百分之九十二的人了。而且可能这种时间长的速度不稳定。提交几次几次不同的结果,但是因为最好的成绩超过百分之九十二,所以我这道题就这样了,贴上最后版的代码:
class Solution {
public List> threeSum(int[] nums) {
List> res = new ArrayList>();
Arrays.sort(nums);
//当第i个元素大于0说明已经不可能凑成三元组了
//所以这里一定是主动break,无所谓i的范围
for(int i = 0;
i0) break;
//第一个正常算,第二个开始直接continue
if(i>0 && nums[i]==nums[i-1]) continue;
int l = i+1;
//第二个元素
int r = nums.length-1;
//第三个元素
//这里用了双指针,不断往中间计算,省去了两次循环
while(ll && nums[r]==nums[r-1]) r--;
//注意这里是除了while还要加加减减,因为不相等的时候都不进while
l++;
r--;
}else if (sum>0) {//大于0说明右边的大了,往左移指针
r--;
}else {//小于0说明左边的小了,往右移指针
l++;
}
}
}
return res;
}
}
最接近的三数之和
题目:给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2). 思路:这个题目如此简洁。。。啧啧啧。其次上道题是三数之和,这道题是最接近的三数之和。有区别,但是又好像是有联系。我不知道能不能用上道题的思路来解,但是起码要试试。我去试试。
我不知道要怎么说,结果这道题很稳,借鉴上一道题的思路。一次过了,但是性能就不是那么好,只超过了百分之八十五的人!算了,我先贴出第一版代码:
clclass Solution {
public int threeSumClosest(int[] nums, int target) {
int res = Integer.MAX_VALUE;
boolean flag = true;
Arrays.sort(nums);
for(int i = 0;
itarget){
if(sum-target
自己优化了半天也没进步,最好结果也就是百分之八十五,我直接去看排行第一的代码吧。
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int result = Integer.MAX_VALUE,left,right,min = Integer.MAX_VALUE;
for (int i = 0;
i < nums.length - 2;
i++) {
left = i + 1;
right = nums.length - 1;
int rangemax = nums[i] + nums[right] + nums[right - 1];
int rangemin = nums[i] + nums[left] + nums[left + 1];
if (rangemin > target) {
if (rangemin - target < min) {
min = rangemin -target;
result = rangemin;
}
} else if (rangemax < target) {
if (rangemax - target < min) {
min = target - rangemax;
result = rangemax;
}
} else {
while (left < right) {
int sum = nums[left] + nums[i] + nums[right];
if (sum > target) {
right--;
} else if (sum < target) {
left++;
} else {
return sum;
}
if (Math.abs(sum - target) < min) {
min = Math.abs(sum - target);
result = sum;
}
}
}}
return result;
}
}
【leetCode进阶算法题+解析(二)】感觉百分之九十的相似度。就是差不多的。只不过人家在我代码的基础上多了两个判断。就是这个双端取值。也就是多了这一步直接性能上来了,这个真的是想的不够多,不是疏忽,是真的没想到。学到了。
今天的笔记就记到这里。昨天刷了四道题,今天五道。是在进步的,加油!另外本篇文章如果稍微帮到你了记得点个喜欢点个关注,也祝大家工作顺顺利利!
推荐阅读
- 画解算法(1.|画解算法:1. 两数之和)
- Guava|Guava RateLimiter与限流算法
- 【Leetcode/Python】001-Two|【Leetcode/Python】001-Two Sum
- leetcode|leetcode 92. 反转链表 II
- 一个选择排序算法
- SG平滑轨迹算法的原理和实现
- 推荐系统论文进阶|CTR预估 论文精读(十一)--Deep Interest Evolution Network(DIEN)
- 《算法》-图[有向图]
- 二叉树路径节点关键值和等于目标值(LeetCode--112&LeetCode--113)
- LeetCode算法题-11.|LeetCode算法题-11. 盛最多水的容器(Swift)