LeetCode | 704.二分查找
题目:
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
704.二分查找https://leetcode.cn/problems/binary-search/
思考:
- 对于right的初始值:
- nums.length —— 区间右边是开区间的情况取,因为right是数组最后一个数的下标+1,num[right]不在查找范围内;
- nums.length - 1 —— 区间右边是闭区间的情况取,因为right是数组最后一个数的下标,num[right]在查找范围内;
- 对于while的条件判断:
- left < right —— 区间右边是开区间的情况取,因为区间左右不能相等,否则矛盾;
- left <= right —— 区间右边是闭区间的情况取,区间左右能相等;
- 对于mid的取值:
- (left + right)/ 2 —— 没有考虑最大数溢出以及出现浮点数;
- left +(right - left)/ 2 —— 没有考虑出现浮点数;
- parseInt((left + right) / 2) —— 没有考虑最大数溢出;
- left + ((right - left) >> 1) —— 采用位运算,防溢出的同时消灭小数!
题解:
1.第一种写法(左闭右闭区间 [left, right])
// 解法一(左闭右闭区间)
var search = function(nums, target) {
var left = 0;
// 由右区间的开闭来决定right的初始值
var right = nums.length - 1;
while(left <= right){
// 可以使用parseInt()取整(因为此题给了数字范围)
// var mid = parseInt((right+left)/2)
// 或位运算取整(可以防止最大数溢出)
var middle = left + ((right - left) >> 1)
if(nums[mid] > target){
// 这里的更新也与区间的开闭有关
right = mid - 1
}else if(nums[mid] < target){
left = mid + 1
}else {
return mid
}
}
return -1
};
2.第二种写法(左闭右开区间 [left, right))
// 解法二(左闭右开区间)
var search = function(nums, target) {
var left = 0;
var right = nums.length;
while(left < right){
var mid = left + ((right - left) >> 1)
if(nums[mid] > target){
right = mid
}else if(nums[mid] < target){
left = mid + 1
}else {
return mid
}
}
return -1
};
二分法思路总结:
- 首先二分法的使用前提是有序数组,看到有序数组都要第一时间考虑是否可以使用二分法(二分法的最大优势是其时间复杂度是O(logn));
- 其次注意边界的情况讨论,要结合区间的开闭进行分析:
- 当l = 0, r = n的时候因为r这个值我们在数组中无法取到,while(l < r) 是正确写法;
- 当l = 0, r = n - 1的时候因为r这个值我们在数组中可以取到,while(l <= r) 是正确写法 主要看能不能取到这个值;
LeetCode | 27.移除元素
题目:
给你一个数组nums和一个值val,你需要原地移除所有数值等于val的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用O(1)额外空间并原地修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
27.移除元素https://leetcode.cn/problems/remove-element/思考:
- 想过先进行排序,然后再移除元素,但是感觉排序过程增加了时间复杂度,所以放弃这个思路;
- 然后进行暴力破解,运用for循环与数组的splice()方法结合(相当于两层for循环暴力破解),但是复杂度仍然很大;
- 最后使用的是双指针法,用一个for循环解决掉两个for循环做的事情。
题解:
var removeElement = function(nums, val) {
// 定义一个慢指针a
var a = 0;
// 定义一个快指针i
for(var i = 0; i < nums.length; i++){
// 如若不是val,则进行覆盖,让元素保留在数组中
if(nums[i] != val){
// 覆盖后更新a指针 —— a++
nums[a++] = nums[i]
}
}
// a的数值即为数组的新长度
return a
};
双指针法思路总结:
- 双指针法(快慢指针法)是指通过一个快指针和慢指针在一个for循环下完成两个for循环的工作;
- 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组;
- 慢指针:指向更新新数组下标的位置;
- 在数组和链表的操作中是非常常见,可以解决用两个for循环时,时间复杂度高的问题;
- ps:数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖;