开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情”
这些东西来来回回看,忘了看,看了忘🤦。
二分查找
原理:
二分查找的前提条件就是,单调增,无重复数
- 如果
target<middle(数列中间的那个值),那么将意味着middle后面所有值都大于target,所以只需要对middle之前进行比较 - 同理,对于
target>middle,那么只要对middle之后进行比较 - 如果
target=middle,那么就说找到了这个值
细节处理
关于是[,]还是[,)区间对应的不同解答
[left,right)
我的理解是right值永远不会是target对应下标的,每次搜索区间是[left,right-1]。
-
初始化,
right = nums.length因为
nums[nums.length]也是永远无法取到的 -
while(left<right)因为,跳出
while循环的时候,条件是left === right,[left,left)这个区间是没有遗漏的。假如这个区间是[left,right],那么结束条件变为[left,left],这时候其实是遗漏了left这个值的。 -
并且当
target<nums[mid]时,我们的right = mid因为我们已经知道
nums[mid],一定不会等于target。也就是下一次我要查找的区间其实是[left,right-1]或者[left,right)
[left,right]
我的理解是:right是可能作为target对应下标的,也就是每次搜索区间是[left,right]
-
right其实是可以取到的,所以他其实是数组最后一个元素的下标right = nums.length-1 -
while(left<=right),跳出循环的条件是left === right+1,也就是[left,left+1],没有遗漏元素。同理,假如区间为[left,right),那么结束条件变为了[left,left+1),那么left就遗漏了。 -
那么当
target<nums[mid]时,我们的right = mid-1正如上面说的,
nums[mid],一定不会等于target。查找的区间其实是[left,mid-1],所以我们将right=mid-1
二分查找
题目:[二分查找](704. 二分查找 - 力扣(LeetCode) (leetcode-cn.com))
题解:
-
为了方便显示,我们将
mid用来显示中间的值 -
注意,我们取中间的值的时候都是使用
Math.floor() -
这是数学问题,取中间那个值
mid = left + Math.floor((right - left) /2)。但是我们推荐使用
mid = left + Math.floor((right - left) /2),因为如果你使用Math.floor((left + right) / 2),可能会出现,left和right太大,然后相加出现溢出的情况
代码
function search(nums: number[], target: number): number {
let right = nums.length - 1;
let left = 0;
let mid = 0;
while (left <= right) {
mid = left + Math.floor((right - left) /2);
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
return mid;
}
}
return -1;
}
搜索插入位置
题解:
-
主要思路就是一个二分查找+插入
-
如果可以找到这个值,那么皆大欢喜,直接返回下标即可
-
如果没有扎到这个值,那么就意味着,已经跳出来
while循环left === right+1了,才会跳出循环。 -
这时候,将
target放在left上最合适。因为刚刚已经搜索过了,left后面的值后面的值都是比target大的
代码:
function searchInsert(nums: number[], target: number): number {
// 二分查找+插入值
let left = 0;
let right = nums.length - 1;
while (left <= right) {
let mid = left + Math.floor((right - left) / 2);
if (target === nums[mid]) {
return mid
} else if (target < nums[mid]) {
right = mid-1
} else if (target > nums[mid]) {
left = mid+1
}
}
return left
};
在排列数组中查找元素的第一个和最后一个
题目:34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)
思路:
- 其实是套了二分查找的外衣而已,因为二分查找,需要保证,单调增且无重复
- 我们可以首先先找到
target元素,然后在它的前后进行找 - 最后返回范围即可
[sleft+1,sright-1] - 为什么需要
+1,-1呢,因为当最后一个满足nums[sleft] === target的时候,sleft--了,但是其实nums[sleft--] !== target,所以需要+1,同理,sright也是
代码:
function searchRange(nums: number[], target: number): number[] {
let left = 0;
let right = nums.length-1;
while (left <= right) {
let mid = left + Math.floor((right - left) / 2);
if (nums[mid] < target) {
left = mid+1
} else if (nums[mid] > target) {
right = mid-1
} else if (nums[mid] === target) {
// 这时候去查看前后
let sleft = mid;
let sright = mid;
while (nums[sleft] === target) {
sleft--
}
while (nums[sright] === target) {
sright++
}
return [sleft+1,sright-1]
}
}
return [-1,-1]
};
当然,其实还有一种思路,并且其实方法更好,但是我觉得,不那么好理解。
移除元素
要知道数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
移除元素
思路:
- 使用的是一种双指针的思想
slow和fast同时前进,只有当nums[fast] !== val,才会对nums[slow]进行设置值。- 我的理解是:
- 假如题目运行使用两个数组去完成。
- 那么就可以在一次遍历的时候,
current !== val,将他放到新的数组里面。 - 其实我们
nums[slow] = nums[fast]也是差不多的意思。我们现在想要维护的数组,其实只有nums[0...slow]
- 因为数组在内存上连续的,所以不存在,所以其实不存在将数组中间的那个值删掉,其实本质上,他只是将你将要删除的值覆盖掉了。
- 并且题目表示,你不需要考虑数组中超出新长度后面的元素。意思就是,后面多出来的,
fast-slow个值,可以不考虑。
代码:
function removeElement(nums: number[], val: number): number {
// 使用快慢指针
let fast = 0;
let slow = 0;
// 两者是一起走的
while (fast < nums.length) {
if (nums[fast] !== val) {
nums[slow] = nums[fast]
slow++
}
fast++;
}
return slow
};