LeetCode面试经典150
~~堆点思路屎在这里
https://leetcode.cn/studyplan/top-interview-150/
189. 轮转数组
第一反应是创建一个额外数组,把后k位数组拿走放到前面来
class Solution{
public void rotate(int[] nums, int k){
//新建数组储存k位,进行移动
int n = nums.length;
k %= n;
int[] temp = new int[k];
System.arraycopy(nums, n - k, temp, 0, k);
for (int i = n-k-1; i >= 0; i--) nums[i+k] = nums[i];
System.arraycopy(temp, 0, nums, 0, k);
}
}
看了题解才发现另一个写法是环状替换,将每个数字向右移动k位,从而达到整个移动的效果
//图片源于官方题解
官方题解提到的对于数学推导理解一定困难的解决方法
将第i位赋值给(i+k)%n位,并将(i+k)%n位赋值给((i+k)+k)%n位……,重复上述的操作直到赋值位回到第i位,若遍历的数字数量达到了n个,则代表遍历结束,反之则让i+1,继续遍历
先执行判断count数量操作,再执行start++操作,否则当nums.length为1时会出现数组越界问题
class Solution {
public void rotate(int[] nums, int k) {
//环状替换
int n = nums.length;
int count = 0, start = 0, temp = nums[start], i, t; //temp储存被替换的数, t储存交换的数, i储存当前位置, count储存交换的次数, start储存起始位置
k %= n;
i = start;
while (count < n) {
i = (i + k) % n;
//交换temp和nums[(start+k)%n]
t = nums[i];
nums[i] = temp;
temp = t;
count++;
if (count == n) break;
//判断是否回到原点,判断是否已经交换了n个数
if (i == start) {
start++;
i = start;
temp = nums[start];
}
}
}
}
121. 买卖股票的最佳时机
简单的题目,设置minn,maxn,ans三个变量,如果有更低的价格则更新ans和minn,如果有更高的价格则更新maxn和ans
class Solution {
public int maxProfit(int[] prices) {
int maxn, minn, ans = 0;
maxn = minn = prices[0];
for (int i = 1; i < prices.length; i++){
if (prices[i] < minn) {
ans = Math.max(ans, maxn - minn);
minn = maxn = prices[i];
}
if (prices[i] > maxn) {
maxn = prices[i];
ans = Math.max(ans, maxn - minn);
}
}
return ans;
}
}
122. 买卖股票的最佳时机 II
如果价格起伏很大,那么一直持有划算还是购买并买入划算呢? 这里假设操作为买、卖、空三种操作代表买入、卖出和不操作
如价格波动为1536,那么买卖买卖的利润为7,买空空卖的利润为5
易观察到:只要前一天的价格比后一天的高,那么先卖出再买入的利润一定高于持有不卖的利润
那么我们的购买策略应该是这样的
- 如果明天的价格上涨了,我们会选择在今天买入,明天再卖出
- 如果明天的价格下跌了,我们不会进行操作(买明天的股票优于现在买股票)
- 我们也不用考虑最后一天股票用不用卖出的问题了,只在有利润的时候我们才会买彩票
class Solution {
public int maxProfit(int[] prices) {
int profit = 0;
for (int i = 0; i < prices.length - 1; i++)
if (prices[i+1] > prices[i]) profit += prices[i+1] - prices[i];
return profit;
}
}
55. 跳跃游戏
简单的动态规划问题,建立一个长度为nums.length的boolean数组,除了0之外其他的值都为false
从0开始遍历,能到达的位置设为true
class Solution {
public boolean canJump(int[] nums) {
boolean judge[] = new boolean[nums.length];
judge[0] = true;
for (int i = 0; i < nums.length; i++)
if (judge[i])
for (int j = 1; j <= nums[i]; j++)
if (i + j < nums.length) judge[i + j] = true;
return judge[nums.length - 1];
}
}
然后发现运行时间过于长了,代码的时间复杂度是 O ( n 2 ) O(n^2) O(n2) 其中 n 是数组 nums 的长度。这是因为使用了两个嵌套的循环。外层循环遍历数组中的每个元素,内层循环根据当前位置能够跳跃的步数,更新布尔数组 judge。
可以只用一个变量来表示可以到达的长度,这样也能够代表跳跃的长度
class Solution {
public boolean canJump(int[] nums) {
int k = 0;
for (int i = 0; i < nums.length; i++){
if (i > k) return false;
k = Math.max(k, i + nums[i]);
}
return true;
}
}
45. 跳跃游戏 II
怪不得刚刚控制不住自己建了一个boolean数组,原来是写过这道题
简单的动态规划问题,建立一个长度为nums.length的数组
从0开始遍历,能到达的位置更新最小跳跃数为当前位置+1
class Solution {
public int jump(int[] nums) {
int minn[] = new int[nums.length];
minn[0] = 1;//等等结果再减一,为了防止0的情况
for (int i = 0; i < nums.length; i++) {
if (minn[i]==0 || nums[i]==0) continue;
for (int j = 1; j <= nums[i]; j++) {
if (i+j >= nums.length) break;
if (minn[i+j] == 0) minn[i+j] = minn[i] + 1;
else minn[i+j] = Math.min(minn[i+j], minn[i] + 1);
}
}
return minn[nums.length-1]-1;
}
}
然后发现运行时间过于长了,看了题解才知道可以在一次循环中解决,其思路是:
- 对于第一次跳跃,记录当前跳跃的最远距离
- 位置在当前跳跃的最远距离时,记录所有位置能到达的新的最远距离
- 位置超过了当前跳跃的最远距离,将新的最远距离设为当前跳跃的最远距离,且跳跃次数+1
class Solution {
public int jump(int[] nums) {
int curDis = 0,jump = 0, maxDis = 0;
for (int i = 0; i < nums.length - 1; i++){//最后一个元素不用跳
maxDis = Math.max(maxDis, i + nums[i]);
if (i == curDis) {
jump++;
curDis = maxDis;
}
}
return jump;
}
}
274. H指数
思路是排序后二分查找
class Solution {
public int hIndex(int[] citations) {
//排序citations
int n = citations.length;
Arrays.sort(citations);
//二分查找
int l = 0, r = n - 1, mid;
while (l <= r){
mid = (l+r) / 2;
if (citations[mid]==n-mid) return n-mid;
else if (citations[mid]>n-mid) r = mid - 1;
else l = mid + 1;
}
return n-l;
}
}
上面的复杂度是 O ( n l o g ( n ) ) O(nlog(n)) O<