当前位置:   article > 正文

整数二分习题:力扣-35,69(c++代码 含思路详解)_力扣上c++如何运行

力扣上c++如何运行

 目录

1.力扣-35搜索插入位置

第一个要求查找目标值:

接下来看第二个要求: 如果数组中不存在目标值,就把目标值按顺序应该在数组中的插入的位置作为答案输出。

2.力扣-69:x的平方根


1.力扣-35搜索插入位置

嗯。读完题之后,我们发现这是一个查找目标值的题,和之前写过的力扣704这道题不同的就是,多增加了一个要求:就是如果数组中不存在目标值,就把目标值按顺序应该在数组中的插入的位置作为答案输出。

第一个要求查找目标值:

那么既然要实现的是查找一个数据。那么我们就可以采用704那种直接的二分方法

1.把while循环条件设置为,数组不为空时一直循环(l<=r)。

2.循环内部,判断语句分三种情况。如果mid所对元素大于目标值,说明应该向左寻找,更新右区间(r=mid-1)。注意既然说了大于了,说明mid的值就不在我们的查找范围内;如果小于目标值,更新左区间(l=mid+1);倘如正好等于,说明mid即为所求,返回即可。

看题中要求。该函数返回一个int类型的数据,所以如上所说,当符合条件时直接renturn即可。 

接下来看第二个要求: 如果数组中不存在目标值,就把目标值按顺序应该在数组中的插入的位置作为答案输出。

想实现该操作,就要考虑如何判断不同情况下的插入位置。在这里,我们所采用的这种简单的二分方法的优势就体现出来了。因为当我们循环结束,没有找到目标值,但这个二分仍然是有一个结果的也就是mid。

这里我们要想清楚一件事。

二分的过程要么直接找到了target,要么就是找到了离mid最近的那个元素

因为我们每次二分区间,都是根据mid与目标元素的大小关系来更改区间的。 

那这样的话,关于插入位置下标的功能就很好实现了。

l>r是循环出口,那么在l>r的上一步:

l=r继续循环,倘若nums[mid]>target,(这时说明我们所需要的下标应该是mid) r=mid-1导致了l>r,那么l是=mid的。

倘若是因为nums[mid]<target,(这时说明我们所需要的下标应该是mid+1) l=mid+1,导致了l>r,那么l=mid+1的。

所以在while循环结束后,倘若没有return,我们直接return l即可。

我感觉这个是非常巧妙的,我在写的时候,没有想到这个,还一直在分情况啊,判断大小啊啥的。比较蠢hhh。

另一个注意点是注意题中的要求:我们并不需要将他真的插入进去,只是想知道插入的位置。所以我的那种复杂的(应该还实现不了的)写法就或许更没必要(??)了。

代码如下:

  1. // 注意点在函数插入的位置
  2. // 二分的过程要么直接找到了target,要么就是找到了离mid最近的那个元素
  3. #include <iostream>
  4. #include <vector>
  5. using namespace std;
  6. class Solution
  7. {
  8. public:
  9. int searchInsert(vector<int> &nums, int target)
  10. {
  11. int n = nums.size();
  12. if (n == 0)
  13. return 1;
  14. int l = 0, r = n - 1;
  15. while (l <= r)
  16. {
  17. int mid = (l + r) / 2;
  18. if (nums[mid] == target)
  19. return mid;
  20. else if (nums[mid] > target)
  21. r = mid - 1;
  22. else
  23. l = mid + 1;
  24. } // 循环终止的时候 l>r
  25. // 在l>r的上一步,l=r继续循环,倘若nums[mid]>target,这时说明我们所需要的下标应该插到mid处,r=mid-1导致了l>r,那么l是=mid的
  26. // 倘若是因为nums[mid]<target,这时说明我们所需要的下标应该插到mid+1处,l=mid+1,导致了l>r,那么l=mid+1的
  27. return l;
  28. // 数组中不存在这个数,将其插入 我们并不需要将他插入,只是想知道插入的位置
  29. /*蠢蛋emm
  30. l=0,r=n-1;
  31. while(l<r)
  32. {
  33. int mid=l+r>>1;
  34. if(nums[mid]>=target)r=mid;
  35. else l=mid+1;
  36. }
  37. if(l=n-1) return l+1;//末尾插入
  38. else if(l<n-1) return l;//数组中插入
  39. else
  40. */
  41. }
  42. };
  43. int main()
  44. {
  45. int n;
  46. cin >> n;
  47. vector<int> nums(n);
  48. for (int i = 0; i < n; i++)
  49. {
  50. cin >> nums[i];
  51. }
  52. int target;
  53. cin >> target;
  54. Solution s;
  55. int ans = s.searchInsert(nums, target);
  56. cout << ans << endl;
  57. return 0;
  58. }

(还把那个蠢得留着,是为了提醒我上面的写法是多么巧妙doge)

ok这道题就到这里了。

2.力扣-69:x的平方根

额,感觉这道题题设比较含蓄,不像前面写的,能直接想到二分的(当然可能是我没记住二分的核心)。

如何想到二分呢?我们看题中说已知非负整数x,求根号x。也就是在【0,x】范围内找到一个目标值(根号x)。嗯,想一会理解一下。

那么接下来,我们开始考虑使用哪一个模板。

首先要先确定本题是要查找一个元素(力扣704) 还是 查找满足某一性质的区域(两个模板)?

我感觉是比较容易误解为找一个数的。那么为什么不是找一个数的呢?因为这道题存在:x并不是完全平方根的 情况,就不可能会有(mid== x/mid)的时候,所以会导致程序出错,也就是你的二分居然没有一个结果??。

排除一个,然后我们就要再看题中的注意点:它说返回类型只保留整数,也就是说像5/2得出结果是2这种意思,也就是应该尽量取小的值。所以我们想一下。我们应该找的是最后一个使得 该数平方之后<=x 的数即使用模板二

当然我们可以举个例子:如果x=8,由于开方之后不是整数,取整之后,按照这道题的意思,应该是取2作为答案,这里我们描述一下2这个答案的性质:也就是最后一个满足平方之后<=8的数。现在理解了哈。

你可能会问为什么不能表述成模板一的说法来求解:如果换成求第一个平方之后>=8的数,那么得到的结果应该是3啊,与我们想要的不同,所以不能使用模板一。

好啦,这里理解了就好写了。毕竟模板我们是很熟悉的嘛(应该是把??)。(模板二的注意事项大家不要写错哦)

代码如下

  1. #include <iostream>
  2. using namespace std;
  3. int x;
  4. int main()
  5. {
  6. cin >> x;
  7. int l = 0, r = x;
  8. while (l < r)
  9. {
  10. long long mid = (l + r + 1) / 2;
  11. if (mid <= x / mid) //避免在 mid 很大时发生整数溢出的问题
  12. l = mid;
  13. else
  14. r = mid - 1;
  15. }
  16. cout << l << endl;
  17. return 0;
  18. }

ps:在这段代码中,我们使用了表达式 (l+r+1)/2 来计算区间的中点。由于 l 和 r 的初始值都小于等于x,且x<=2^31-1,因此 l+r+1 的最大值为2^31 - 1 + 2^31 - 1 + 1 = 2^32 - 1,应该用long long 类型定义

当然按照力扣的答题要求,应该按下面这样写:

  1. #include <iostream>
  2. using namespace std;
  3. class Solution
  4. {
  5. public:
  6. int mySqrt(int x)
  7. {
  8. if (x < 2)
  9. return x;
  10. long long l = 0, r = x; // 避免溢出当l r需要做加法的时候
  11. while (l < r)
  12. {
  13. long long mid = l + (r - l + 1) / 2;
  14. // int mid = (l + r + 1ll) / 2;
  15. // 这里 1ll 的写法是直接把将l+r整个表达式的类型提升为 long long 避免了溢出 倘若这样写,那么前面l r也就不需要定义为longlong了
  16. if (mid <= x / mid)
  17. l = mid;
  18. else
  19. r = mid - 1;
  20. }
  21. return l;
  22. }
  23. };
  24. int main()
  25. {
  26. int x;
  27. Solution s;
  28. int ans = s.mySqrt(x);
  29. cout << ans << endl;
  30. return 0;
  31. }

其实是有个疑问的,为什么 long long mid = l + (r - l + 1) / 2; 这里不定义为 long long 类型会一直报错,显示溢出, l + (r - l + 1) / 2这种写法不是已经避免了+法么?为什么还会溢出呢?求大佬指点

好啦,这道题就到这里啦。


 有问题欢迎指出,非常感谢!!!

也欢迎交流建议奥。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/pjghi/article/detail/62808
推荐阅读
相关标签
  

闽ICP备14008679号