概述
由LeetCode官方推出的经典面试题目清单,将题目重新整理规划,从而为大家提供更好的练习体验和帮助大家找到理想的工作。将题目分为以下三个部分:
- 初级算法-> 帮助入门 自己数了一下有49个题呀
- 中级算法-> 巩固训练
- 高级算法-> 提升进阶
数组
1.删除排序数组中的重复项 2020/08/13
给定一个排序数组,需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。不要使用额外的数组控件,你必须在原地修改数组,并使用O(1)额外空间的条件下完成。
输入:nums =[1,1,2]
函数应该返回新的长度2,并且原数组nums的前两个元素被修改为1,2。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
注意:输入数组是以引用的方式传递的,意味着在函数里修改输入数组对于调用者是可见的。
可以想象内部操作如下:
目前想法:首先明确,该数组是有序的,并且在原地修改数组数组,使用O(1)的额外空间条件下完成,且不需要考虑超出新长度后面的元素。
感觉挺简单的,由于有顺序,设定一个值用于给数组重新赋值(每当数组当中元素比之前的元素大的时候),最后返回这个值就可以了。
这个nums没有给定范围是有可能为空数组的,因此需要加判断。
2.买卖股票的最佳时机 II
给定了一个数组,其第i个元素是一支给定股票第i天的价格
设计一个算法来计算所获取的最大利润。可以尽可能地完成更多的交易(多次买卖一次股票)。注意:不能同事参与多笔交易。
输入:【7,1,5,3,6,4】
输出:7
解释:第2天买入,第3天卖出,利润为5-1=4.。
第4天买入,第5天卖出,利润为6-3=3。
目前的想法是:定义两个指针i,j(i<j),并且保证2点:prices[i]<prices[j] 和prices[j]>prices[j+1],然后让I=j。现在开始尝试去解答这个题。
最后做出来和开始的想法还是有点不同的。
看了下别人的题解,可以用暴力、dp、贪心(我这个也是贪心,不过写复杂了)。
大佬写的动态规划代码,对于动态方程的转移太美了。下面记录 下:
我的理解是,就是构建了两个数组,第一个保存第i天所持有的现金,第二个来保存第i天所持有的股票。
所以初始值就是cash[0]=0,hold[0]=-prices[I],因为买了第一天的股票。
然后第二天cash[1] = max(cash[0],hold[0]+prices[1]),比较前一天的最大现金,和将上一天股票抛售所获的的利润和。
hold[1] = max(hold[0],cash[0]-prices[1]),比较前一天所持有的股票和昨天持有的现金买入今天的股票的值。
定义为卖出和买入可能会更好理解一些。
3.旋转数组
2020/08/15 8点53.start
首先还是第一步,读题:给定了一个数组,将数组中的元素向右移动k个位置,其中k是非负数。
输入:【1,2,3,4,5,6,7】和k=3
输出:【4,5,6,7,1,2,3】
解释:向右旋转1步:【7,1,2,3,4,5,6】
向右旋转2步:【6,7,1,2,3,4,5】
向右旋转3步:【5,6,7,1,2,3,4】
说明:要求想出尽可能多的解决方案,至少有三种步同的方法可以解决这个问题。
要求使用空间复杂度为O(1)的原地算法。
最先想到的算法是之前做杭电oj提供的那种思路:
旋转3次:【7,6,5,4,3,2,1】
【5,6,7】 【1,2,3,4】。
先完成这种方法吧。
没有考虑到nums =[1] k=0 这种情况,也没有考虑到nums=[-1] k=2这种情况。
class Solution {
public void rotate(int[] nums, int k) {
k = (k + nums.length)%nums.length;
if(k > 0 && k<nums.length) {
// 第一种方法,颠倒数组当中第left和right。
reverse(nums,0,nums.length-1);
reverse(nums,0,k-1);
reverse(nums,k,nums.length-1);
}
}
public void reverse(int[] nums,int left,int right){
int medium = (right+left)/2;
int temp;
for(int i = left; i<= medium; i++){
temp = nums[i];
nums[i] = nums[left+right-i];
nums[left+right-i] = temp;
}
}
}
自己做的第一种方法。
开始没有去考虑k的取值范围,没有去考虑k=0以及k>=nums.length的情况。
考虑以后就是,先把k转为为k=(k+nums.length)%nums.length,然后在分情况考虑。
下面来记录官方给的几种题解:
还蛮喜欢这种暴力解的思路,旋转k次,每次将数组旋转一个元素(这里用了一个previous来记录每个位置应该放的数,用一个temp来保存当前的值,然后更新这个previous)。
构建一个额外的数组来将每个元素放到正确的地方,即将原本数组里下标为i的我们把它放到(i+k)%数组的长度的位置。然后把新的数组拷贝到原数组即可。
核心:a[(I+k)%nums.length] = nums[I]。
这种方法很巧妙,mark!
4.存在重复元素
2020/08/16 10:02 start
给定了一个整数数组,判断是否存在重复元素。
如果任意一值在数组中出现至少两次,函数返回true。如果数组中每个元素都不相同,则返回false;
输入:【1,2,3,1】
输出:true
输入:【1,2,3,4】
输出:false
目前想法:构建一个hashset,如果有之前存在的数字,则返回true。。。现在开始尝试写代码。
这个速度,我人傻了,9ms。
看了别人用的hashes,发现自己就是一个脑瘫。。。直接
这个地方不必要用contains方法,add(object)方法返回都是boolean,如果Set集合中已经包含相同的对象,则不改变Set集合。如果Set集合中不包含添加的对象,则添加对象并返回true,否者返回false。
因此代码可以改为:
执行时间为6ms。
然后还可以采用sort函数进行排序,然后来判断是否有重复的数字。
采用sort函数,运行时间是4ms。
然后执行时间为3ms的思路是,首先去获取nums数组中的最大值和最小值,然后新建一个数组(hash)来判断有没有重复元素。
执行时间为2ms的思路是对每一个元素,判断这个元素与他之前有没有重复的元素。
1ms和2ms的代码一样,这里不列了。
执行用时为0ms的不列了,本质还是hash。