当前位置:   article > 正文

优选算法之二分查找

优选算法之二分查找

一,简介

二分查找是细节比较多的,但是你理解细节那么二分查找可以直接秒杀的。二分查找是必须有序才能用吗?答案显然是不太正确的。更准确的说是具有二段性便可以使用二分查找,而且二分查找是有模板的。接下来我将会介绍一些二分查找的类型题目,来帮助大家理解并快速上手二分查找。

二,模板类型

二分查找分为三个模板类型,分别是朴素二分模板,寻找左区间的二分模板,寻找右区间的二分模板。

就拿这一题来讲解朴素模板:704.二分查找 

  1. class Solution {
  2. public:
  3. int search(vector<int>& nums, int target) {
  4. int left = 0, right = nums.size() - 1;
  5. //先判断范围
  6. while (left <= right)
  7. {
  8. int mid = (left + right) / 2;
  9. //判断左右区间
  10. if (nums[mid] > target)
  11. {
  12. right = mid - 1;
  13. }
  14. else if (nums[mid] < target)
  15. {
  16. left = mid + 1;
  17. }
  18. else
  19. {
  20. return mid;
  21. }
  22. }
  23. return -1;
  24. }
  25. };

总结:

朴素模板一般不常用记忆即可。

这一题讲解左右端点模板:在排序数组中查找元素的第一个和最后一个位置

  1. class Solution {
  2. public:
  3. vector<int> searchRange(vector<int>& nums, int target) {
  4. int left = 0, right = nums.size() - 1;
  5. int begin, end;
  6. //细节处理如果数组为空,返回-1,-1
  7. if (nums.size() == 0)
  8. {
  9. return { -1,-1 };
  10. }
  11. //进入循环条件
  12. while (left < right)
  13. {
  14. //写出中间值
  15. int mid = left + (right - left) / 2;
  16. if (nums[mid] < target)
  17. {
  18. left = mid + 1;
  19. }
  20. else
  21. {
  22. right = mid;
  23. }
  24. }
  25. //判断数组左端点
  26. if (nums[left] == target)
  27. {
  28. begin = left;
  29. }
  30. else //没有则直接返回-1,-1
  31. {
  32. return { -1,-1 };
  33. }
  34. //判断数组右端点
  35. left = 0, right = nums.size() - 1;
  36. while (left < right)
  37. {
  38. int mid = left + (right - left + 1) / 2;
  39. if (nums[mid] <= target)
  40. {
  41. left = mid;
  42. }
  43. else
  44. {
  45. right = mid - 1;
  46. }
  47. }
  48. return { begin,right };
  49. }
  50. };

模板总结:

只要写出左右端点模板即可完成编码,当判断左右端点时,如果是mid+1则mid为left+(right-left)/2,如果是mid-1则mid为left+(right-left+1)/2。

三,例题

其它代码分类讨论,接下来我就带大家熟悉二分模板。

例题:35.搜索插入位置

  1. class Solution {
  2. public:
  3. int searchInsert(vector<int>& nums, int target) {
  4. int left=0,right=nums.size()-1;
  5. //进入循环条件
  6. while(left<right)
  7. {
  8. //根据左右端点判断mid
  9. int mid=left+(right-left)/2;
  10. //判断左端点
  11. if(nums[mid]<target)
  12. {
  13. left=mid+1;
  14. }
  15. else
  16. {
  17. right=mid;
  18. }
  19. }
  20. //处理细节,如果没有则插入最后一个位置
  21. if(nums[left]<target)
  22. {
  23. return left+1;
  24. }
  25. else
  26. {
  27. return left;
  28. }
  29. }
  30. };

例题:x的平方根

  1. class Solution {
  2. public:
  3. int mySqrt(int x) {
  4. //处理细节,x小于1,0的平方为0
  5. if(x<1)
  6. {
  7. return 0;
  8. }
  9. //x大于等于1
  10. int left=1,right=x;
  11. while(left<right)
  12. {
  13. //判断右端点
  14. long long mid=left+(right-left+1)/2;
  15. if(mid*mid<=x)
  16. {
  17. left=mid;
  18. }
  19. else
  20. {
  21. right=mid-1;
  22. }
  23. }
  24. return right;
  25. }
  26. };

例题: 

​​​​​​山脉数组的峰顶索引

  1. class Solution {
  2. public:
  3. int peakIndexInMountainArray(vector<int>& arr) {
  4. int left=0,right=arr.size()-1;
  5. while(left<right)
  6. {
  7. //判断右端点
  8. int mid=left+(right-left+1)/2;
  9. if(arr[mid]>arr[mid-1])
  10. {
  11. left=mid;
  12. }
  13. else
  14. {
  15. right=mid-1;
  16. }
  17. }
  18. return right;
  19. }
  20. };

总结:这个题就很好表达了二分查找的使用有序并不是必要条件

例题:

寻找峰值

和上一题一样找清楚判断左右端点对于mid的关系

  1. class Solution {
  2. public:
  3. int findPeakElement(vector<int>& nums) {
  4. int left=0,right=nums.size()-1;
  5. while(left<right)
  6. {
  7. //判断左端点
  8. int mid=left+(right-left)/2;
  9. if(nums[mid]<nums[mid+1])
  10. {
  11. left=mid+1;
  12. }
  13. else
  14. {
  15. right=mid;
  16. }
  17. }
  18. return left;
  19. }
  20. };

例题: 

寻找旋转排序数组中的最小值

该题需要了解数组是怎样旋转的,分为ab,cd两个段。如果第一个值大于最后一个说明最小值在cd段,反之在ab段,这样就可以判断left和right怎么变化。

  1. class Solution {
  2. public:
  3. int findMin(vector<int>& nums) {
  4. int left=0,right=nums.size()-1;
  5. int n=nums.size();
  6. while(left<right)
  7. {
  8. int mid=left+(right-left)/2;
  9. if(nums[mid]>nums[n-1])
  10. {
  11. left=mid+1;
  12. }
  13. else
  14. {
  15. right=mid;
  16. }
  17. }
  18. return nums[left];
  19. }
  20. };

例题:

点名

该题因为不限制时间所以有多种解法

  1. class Solution {
  2. public:
  3. int takeAttendance(vector<int>& records) {
  4. int n=records.size();
  5. int left=0,right=n-1;
  6. while(left<right)
  7. {
  8. int mid=left+(right-left)/2;
  9. if(records[mid]==mid)
  10. {
  11. left=mid+1;
  12. }
  13. else
  14. {
  15. right=mid;
  16. }
  17. }
  18. //处理细节问题,如果是最后一个数确实需要加1
  19. if(records[left]==left)
  20. {
  21. left++;
  22. return left;
  23. }
  24. else
  25. {
  26. return left;
  27. }
  28. }
  29. };

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号