目录
704 二分查找
看到题目的第一想法:这道题之前刷过,采用二分法来获取有序数组某个元素的位置,但是边界的处理上感觉自己处理的很不规范,而自己也没有好的解决方法。
看完代码随想录之后的想法:卡哥的循环不变量有效解决了边界处理上模棱两可的问题,在做二分查找时,可以明确的规定左闭右闭或者左闭右开的边界处理规则,这对算法的初学者很友好。
- 此处的循环不变量即区间的左右边界是明确的,是已经想清楚的。
代码实现:
区间为左闭右闭时:
class Solution {
public int search(int[] nums, int target) {
//二分查找,左闭右闭
int length = nums.length;
int left = 0, right = length - 1, mid;
while (left <= right) {
//防止越界,为什么这样可以防止越界?
mid = left + (right - left) / 2;
if (target == nums[mid]) {
return mid;
} else if (target > nums[mid]) {
left = mid + 1;
}else {
right = mid - 1;
}
}
return -1;
}
}
区间为左闭右开时:
class Solution {
public int search(int[] nums, int target) {
//二分查找,左闭右开
int length = nums.length;
int left = 0, right = length, mid;
while (left < right) {
//防止越界,为什么这样可以防止越界?
mid = left + (right - left) / 2;
if (target == nums[mid]) {
return mid;
} else if (target > nums[mid]) {
left = mid + 1;
}else {
right = mid;
}
}
return -1;
}
}
实现过程中遇到哪些困难:
- while循环的终止条件:left <= right or left < right ?
- 对于左闭右闭区间,left == right 即指向同一个元素时,是需要进入while循环处理具体逻辑的,可以拿长度为1的数组模拟一下。
- 对于左闭右开区间,同样拿长度为1的数组模拟一下,若left == right,则唯一的元素始终无法进入while循环,这显然是有问题的,故使用left < right。
- mid的取值:为什么使用mid = left + (right - left) / 2 就能防止溢出,而mid = (left + right) / 2就可能会导致溢出
- 此处的越界应该是相对于int的取值范围而言,(left + right)可能超过int的取值范围,造成溢出,影响计算的准确性;而mid = left + (right - left) / 2的写法显然没有溢出问题。
27 移除元素
看到题目的第一想法:这道题(原地的移除元素)之前也刷过,看到题后没想到用双指针法,并且暴力解法也没有思路。
看完代码随想录之后的想法:
- 双指针法:fast指针一直往右走,若遇到指针指向的值与val不相等时,则将此处的值覆盖slow所指向位置的值,最终数组中所有元素的值都不等于val,且slow此时的值即为新数组的长度。
- 暴力解法:需要两层循环,第一层循环先寻找与val相等的元素值,找到后,采用第二层循环将此元素值后方的值整体向前移动一位,并且第一层循环的轮次要-1,因为之前已经自增1,-1是为了让 i 再次回到之前值,并且数组size-1。
代码实现:
双指针法
class Solution {
public int removeElement(int[] nums, int val) {
//暴力解法暂时没想到,后参照代码随想录里的暴力解法
//快慢指针法
int fast= 0, slow = 0;
int length = nums.length;
while (fast < length) {
if (val != nums[fast]) {
if (fast != slow) {
nums[slow] = nums[fast];
}
slow++;
}
fast++;
}
//为什么不是slow-1,以3 2 2 3 val=3为例,slow从0开始,slow=2时结束循环,此时长度刚好为2,而非2-1
return slow;
}
}
暴力解法
class Solution {
public int removeElement(int[] nums, int val) {
//暴力解法暂时没想到,后参照代码随想录里的暴力解法
//暴力法:遍历数组,遇到与val相等的元素值时,整体左移一位,遍历轮次值-1,size-1
//使用双层for循环
int size = nums.length;
for (int i = 0; i < size; i++) {
//第一层循环查看是否有要移除的元素,若有,才进入第二层循环,否则没有必要
if (nums[i] == val) {
for (int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
//循环位置需还原,即左移一次
i--;
//数组长度-1
size--;
}
}
return size;
}
}
实现过程中遇到哪些困难:
- 双指针法何时移动slow指针?
- 当val != nums[fast]时,需要首先①将fast指针的值覆盖slow指针指向的值,②而后slow++,如果顺序为②①,则会导致有些等于val的元素未能被移除。
- 暴力解法:为什么要先判断val == nums[i],而后进入第二层循环,而不知进入第二层循环后再判断val == nums[i] ?
- 一层循环后直接判断val与nums[i]是否相等,可以减少后面循环的次数,一定程度上降低了时间复杂度。
今日收获,记录一下自己的学习时长
- 算法处理约2h,博客编写约1.5h
- 熟悉了二分查找与双指针法,编写博客也是对自己思路的梳理~
- 贵在坚持,加油!