赞
踩
讲解视频:手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找_哔哩哔哩_bilibili
文章讲解:代码随想录 (programmercarl.com)
以下两种写法都可以完成LeetCode704。
拓展题目:35、34、69、367
二分法的写法分两种:左闭右闭写法[left,right]和左闭右开写法[left,right)。
根据写法的不同,来确定:
① while(left<right)
还是while(left<=right)
?
②right=mid
还是right=mid-1
?
核心:区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。
时间复杂度O(log n),空间复杂度O(1)
int bSearch1(int nums[], int len, int target) { int left = 0, right = len - 1; while (left <= right) { //注意点1:这里用<=。原因:这是左闭右闭写法,假设left=right=1时,[left,right]=[1,1]成立,故这里取等于。 int mid = (left + right) / 2; if (nums[mid] > target) { right = mid - 1;//注意点2:这里为mid-1。原因:由于nums[mid]!=target,此时nums[mid]已经不在待选区间内,这是左闭右闭写法,若令right=mid,则会将已经不在待选择区间内的元素nums[mid]引入到下一轮待选区间中。 } else if (nums[mid] < target) { left = mid + 1;//这里与注意点2同理。 } else { return mid; } } return -1; }
时间复杂度O(log n),空间复杂度O(1)
int bSearch2(int nums[], int len, int target) { int left = 0, right = len; //注意点0:这里right=len。因为是左闭右开,右边不包含。 while (left < right) { //注意点1:这里用<。原因:这是左闭右开写法,假设left=right=1时,[left,right)=[1,1)不成立,故这里不取等于。 int mid = (left + right) / 2; if (nums[mid] > target) { right = mid;//注意点2:这里为mid。原因:这是左闭右开写法[left,right),也就是不包含nums[right]。此时nums[mid]>target,说明下一个搜索区间是不包含nums[mid]。故令mid=right即可。 } else if (nums[mid] < target) { left = mid + 1;//注意点3:这里为mid+1。原因:这是左闭右开写法[left,right),即包含nums[left]。此时nums[mid]<target,说明下一个搜索区间是不包含nums[mid],故令mid=left+1。 } else { return mid; } } return -1; }
LeetCode34, 中等题,值得收藏。
思路:代码和文章讲解中几乎相同,改了点地方,让自己更好理解。
!寻找target在数组里的左右边界,有三种情况:
- 情况一:target 在数组范围的右边或者左边,例如数组{3, 4, 5},target为2或者数组{3, 4, 5},target为6,此时应该返回{-1, -1}
- 情况二:target 在数组范围中,且数组中不存在target,例如数组{3,6,7},target为5,此时应该返回{-1, -1}
- 情况三:target 在数组范围中,且数组中存在target,例如数组{3,6,6,7},target为6,此时应该返回{1, 2}
接下来,就是找target的左右边界,左右边界分别用两个函数来寻找,避免混乱。
①寻找左边界往左一个位置(!!注意:不是左边界)
/*找到左边界的往左一个位置*/ int getLeft(int nums[], int len, int target) { int left = 0, right = len - 1; while (left <= right) { int mid = (left + right) / 2; if (nums[mid] > target) { right = mid - 1; } else if (nums[mid] < target) { left = mid + 1; } else {//!若找到target,此时nums[mid]==target,【target左边界往左一个位置】在mid左边,故将候选框仍然向左移动 right = mid - 1; } } return right;//!最终,候选框的right就是【target左边界往左一个位置】 }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
②寻找右边界往右一个位置(!!注意:不是右边界)
/*找到右边界的往右一个位置*/ int getRight(int nums[], int len, int target) { int left = 0, right = len - 1; while (left <= right) { int mid = (left + right) / 2; if (nums[mid] > target) { right = mid - 1; } else if (nums[mid] < target) { left = mid + 1; } else {//!若找到target,此时nums[mid]==target,【target右边界往右一个位置】在mid右边,故将候选框仍然向右移动 left = mid + 1; } } return left;//!最终,候选框的left就是【target右边界往右一个位置】 }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
当获取到target的
左边界往左一个位置
和右边界往右一个位置
后,在main()中对以上三种情况分别处理:int main() { int nums[6] = { 5,7,7,8,8,10 }, target = 8, len = 6;//题目 //!这里之所以初始化为-2而不是-1的原因:getLeft()找的是最左端再往左一个位置,值有可能是-1;rigth_pos只要是负数即可 int left_pos = -2, right_pos = -2; //left_pos:左边界往左一个位置,right_pos:右边界往右一个位置 left_pos = getLeft(nums, len, target); right_pos = getRight(nums, len, target); //情况一:target 在数组范围的【右边+1位置】或者【左边-1位置】 if (left_pos == -2 || right_pos == -2) { cout << "[-1,-1]"; return 0; } //!情况三:若数组中存在target,即使只有一个,right_pos-left_pos也等于2,故必大于1。 if (right_pos - left_pos > 1) { cout << left_pos + 1 << "," << right_pos - 1; return 0; } //情况二 cout << "[-1,-1]"; return 0; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
讲解视频:数组中移除元素并不容易! | LeetCode:27. 移除元素_哔哩哔哩_bilibili.
拓展题目:26、283、844、977
文章讲解:代码随想录 (programmercarl.com)
题目大概:从数组nums=[0,1,2,2,3,0,4,2]中删掉数字2,返回删除后的数组和数组长度。
思路:
用一层for循环实现。 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
将快指针和慢指针放在同一个for循环里,从而实现两个指针都可以向右移动。不过慢指针需要嵌套在if条件下,所以不是每一次for遍历都要移动慢指针;而快指针在每一次for遍历都要移动。
如下图所示,定义快指针fast和慢指针slow:快指针是寻找新数组需要的元素(比如例子中值不为2的元素),然后将该元素赋给新数组当前下标slow所在的位置。慢指针是获取新数组中需要更新的位置。本质上都是在一个数组上操作的。
代码实现
时间复杂度O(n),空间复杂度O(1)
int main() { int nums[4] = { 3,2,2,3 }; int val = 3; int len = sizeof(nums) / sizeof(nums[0]); int slow = 0; //慢指针 for (int fast = 0; fast < len; ++fast) {//快指针的移动 if (nums[fast] != val) {//快指针找到新数组所需的元素,更新新数组 nums[slow] = nums[fast];//将该元素赋给新数组当前下标slow所在的位置 slow++; } } cout << slow << endl; return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。