题目与题解
参考资料:数组理论基础
704. 二分查找
题目链接:704. 二分查找
代码随想录题解:704. 二分查找
视频讲解:
解题思路:
class Solution {
public int search(int[] nums, int target) {
int len = nums.length;
int low = 0, high = len - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}
}
看完代码随想录之后的想法
题目的思路非常容易想到,但实现起来确实如随想录解答所说,对区间的定义没有想清楚。不仅是这道题,许多需要考虑边界条件的问题都很容易出错。
Tips:
- 如果区间是左闭右闭,那么就需要考虑low == high的情况,此时high = mid - 1
如果区间是左闭右开,low 一定不等于 high,此时high = mid
- 代码细节
- 为了防止溢出,最好用low + (high - low)/2 代替 (low + high)/2
- 除以2可以用右移符号 >> 1代替
- 为提高算法效率,可以在一开始加上简单的前提条件避免极端情况
// 避免当 target 小于nums[0] nums[nums.length - 1]时多次循环运算
if (target < nums[0] || target > nums[nums.length - 1]) {
return -1;
}
遇到的困难
第一次写的时候,没有考虑区间的问题,用了low < high作为条件,但是high = mid - 1,于是就华丽丽的出错了。
还有就是很久不从头写代码,在Java中一般都习惯用List方法,突然要用数组类型有点不记得用法,后面题目做多了应该就习惯了。
27. 移除元素
题目链接:27. 移除元素
代码随想录题解:27. 移除元素
视频讲解:
解题思路
数组相关的题,想要提高效率,要么是空间换时间,要么是时间换空间。
首先,最容易想到的就是暴力方法,额外申请一个大小为n的新数组,遍历原数组,将符合要求的数塞入新数组,那么算法空间复杂度为n,时间复杂度为n。
但是题目明确要求,只能原地修改数组,那么就只能用当前数组修改元素,最简单的暴力方法是使用两层for循环,当遍历到的元素等于目标值,则把该元素后面的所有元素往前挪一格,这样时间复杂度就是n2。
如果想将时间复杂度降到n,则要考虑如何在每次循环判断时,只修改少量元素。由于元素的顺序可以修改,那么,可以设置前后两个指针,将当前等于val的元素与后指针指向的元素交换,并将后指针向前挪一格,直到循环到前后指针相遇。
初次解题结果如下:
class Solution {
public int removeElement(int[] nums, int val) {
// 暴力法
int result = nums.length;
int i = 0;
while (i < result) {
if (val == nums[i]) {
result--;
for (int j = i; j < result; j++) {
nums[j] = nums[j+1];
}
} else {
i++;
}
}
return result;
}
}
class Solution {
public int removeElement(int[] nums, int val) {
// 双指针法之交换
int i = 0, j = nums.length - 1;
int result = nums.length;
while (i <= j) {
if (val == nums[i]) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
j--;
} else {
i++;
}
}
return i;
}
}
看完代码随想录之后的想法
最优的快慢指针方法比我的交换法减少了交换的步骤,非常有巧思,既节约了时间,又节约了空间。但是感觉不容易想到,属于看过就会,不看就忘的方法,根本原因还是自己不太理解。
我先尝试看了思路以后自己写,但是不是结果出错,就是边界溢出,纯属没想好瞎碰。快慢指针的关键在于理解两个指针的用处:快指针就是用来遍历数组的,而慢指针用来表示更新数组的下标。换个角度想,结合一开始想到的O(n)空间复杂度的暴力算法,慢指针可以理解为一个新数组的下标,只不过不需要开辟额外的空间,而是直接用新数组覆盖了旧的数组。这种方法还有一个好处:更新后的数组除了去除掉的元素外,顺序不变。
欣赏一下优雅的快慢指针解法:
class Solution {
public int removeElement(int[] nums, int val) {
// 快慢指针
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
if (nums[fastIndex] != val) {
nums[slowIndex] = nums[fastIndex];
slowIndex++;
}
}
return slowIndex;
}
}
遇到的困难
题目难度是不高的,关键还是在于代码实现时要注意细节,什么时候该++,什么时候该赋值,数组下标不要过界,修改了元素定义的范围return的时候也要记得相应修改。
今日收获
看了一下时间,上一次刷题已经是两年前了,那会儿在尝试跳槽,只不过最后没有跳成功,一方面有代码基础薄弱的原因,另一方面也是自己自信不足,在面试中表现不佳。
这两年也断断续续刷过一点题,代码随想录只看了一点,leetcode每日也没有坚持几天。打算从今天开始重拾刷题的脚步,希望后面有机会再去市场上估一估自己的价值。
idea的leetcode插件非常有趣且好用,给解题增添了极大的便利,后面可以考虑上班摸鱼的时候使用:)。
以前没有写博客的时候不觉得,现在写完都要复盘思路和代码实现,就会发现自己非常的粗心,也缺乏耐心,有一点眼高手低,想的太多,做的太少。希望以后补上自己的短板。
今天做题花费的时间大约一小时不到,但是设置和熟悉idea+leetcode插件、分析代码随想录的思路以及写这篇博客,前后加起来也得快两个多小时了。写完第一次有了经验,下一次应该可以提高效率。