[关闭]
@markheng 2018-04-12T15:47:00.000000Z 字数 3916 阅读 939

BinarySearch - 二叉搜索

算法


二叉中重要的地方在于退出循环的条件以及判断边界时的等号是否需要

Template标准板

  1. int search(vector<int> & nums, int target)
  2. {
  3. if(nums.size() == 0) return -1;
  4. int lo = 0, hi = nums.size() - 1;
  5. while(lo <= hi)
  6. {
  7. int mid = lo + (hi - lo) / 2;
  8. if(nums[mid] == target) return mid;
  9. else if(target < nums[mid]) hi = mid - 1;
  10. else lo = mid + 1;
  11. }
  12. return -1;
  13. }

找出下标,不存在则返回-1

数组已排好序
- 可认为数组中没有重复元素

  1. class Solution {
  2. public:
  3. int search(vector<int>& nums, int target) {
  4. if(nums.size() < 1) return -1;
  5. return binarySearch(nums, 0, nums.size() - 1, target);
  6. }
  7. int binarySearch(vector<int>& nums, int lo, int hi, int target)
  8. {
  9. if(lo >= hi && nums[lo] == target) return lo;
  10. if(lo >= hi && nums[lo] != target) return -1;
  11. //prevent lo + hi overflow
  12. int mid = lo + (hi - lo) / 2;
  13. if(target <= nums[mid]){
  14. return binarySearch(nums, lo, mid, target);
  15. }else{
  16. return binarySearch(nums, mid + 1, hi, target);
  17. }
  18. }
  19. };

求开方

  1. int mySqrt(int x) {
  2. long long r = x;
  3. while(r * r > x)
  4. r = (r + x / r) / 2;
  5. return r;
  6. }

0 - x(或x/2)是有序的,所以可以利用折半查找的方法

  1. class Solution {
  2. public:
  3. int mySqrt(int x) {
  4. if(x == 0) return 0;
  5. int lo = 1, hi = x, ans = 0;
  6. while (lo <= hi)
  7. {
  8. int mid = lo + (hi - lo) / 2;
  9. if (x / mid < mid) //防止溢出
  10. {
  11. hi = mid - 1;
  12. }
  13. else
  14. {
  15. lo = mid + 1;
  16. ans = mid;
  17. }
  18. }
  19. return ans;
  20. }
  21. };

旋转排序的数组搜索

旋转排序,即并非从左到右排序,如[4, 5, 6, 7, 1, 2, 3]
该算法思路是将数组转化为[4, 5, 6, 7, INF, INF, INF]或[-INF, -INF, -INF, -INF, 1, 2, 3] (INF表示正无穷)进行折半搜索,

  1. class Solution {
  2. public:
  3. int search(vector<int>& nums, int target) {
  4. int lo = 0, hi = nums.size();
  5. while (lo < hi) {
  6. int mid = (lo + hi) / 2;
  7. double num = (nums[mid] < nums[0]) == (target < nums[0])
  8. ? nums[mid]
  9. : target < nums[0] ? -INFINITY : INFINITY;
  10. if (num < target)
  11. lo = mid + 1;
  12. else if (num > target)
  13. hi = mid;
  14. else
  15. return mid;
  16. }
  17. return -1;
  18. }
  19. };

Find First Peek 找到第一个峰

https://leetcode.com/explore/learn/card/binary-search/126/template-ii/948/

  1. if(nums.size() == 0) return -1;
  2. if(nums.size() == 1) return 0;
  3. if(nums.size() == 2)
  4. {
  5. if(nums[0] < nums[1]) return 1;
  6. else return 0;
  7. }
  8. int lo = 0, hi = nums.size() - 1;
  9. while(lo < hi)
  10. {
  11. int mid = lo + (hi - lo) / 2;
  12. if(nums[mid - 1] < nums[mid] &&
  13. nums[mid] < nums[mid + 1])
  14. {
  15. lo = mid + 1;
  16. }else
  17. {
  18. hi = mid;
  19. }
  20. }
  21. return lo;

找到循环有序的序列的最小数

  1. int findMin(vector<int>& nums) {
  2. if(nums.size() == 0) return -1;
  3. if(nums.size() == 1) return nums[0];
  4. int lo = 0, hi = nums.size() - 1;
  5. while(lo + 1 < hi)
  6. {
  7. if(nums[lo] < nums[hi]) return nums[lo];
  8. int mid = lo + (hi - lo) / 2;
  9. if(nums[0] < nums[mid])
  10. {
  11. lo = mid;
  12. }else{
  13. hi = mid;
  14. }
  15. }
  16. if(nums[hi] >= nums[0]) return nums[lo]; // hi 肯定 != 0,不要用lo与0元素比较,否则无法区分[1, 2]和[2, 1]
  17. else return nums[hi];
  18. }

Search for a Range 搜索范围

Given an array of integers sorted in ascending order, find the starting and ending position of a given target value.

Your algorithm's runtime complexity must be in the order of O(log n).

If the target is not found in the array, return [-1, -1].

For example,

Given [5, 7, 7, 8, 8, 10] and target value 8,
return [3, 4].

正常二分查找设置中点使用的是mid = lo + (hi - lo) / 2,这样会使搜索范围向左移,最终会使得lo = mid而不会使hi = mid,因此容易搜索到target范围的前置哨兵(端点),这里使用一个技巧,使用mid = lo + (hi - lo) / 2 + 1设置中点,可以得到target范围的后置哨兵。
算法两次折半查找,时间复杂度是,空间复杂度是

  1. vector<int> searchRange(vector<int>& nums, int target) {
  2. vector<int> ret(2, -1);
  3. if(nums.size() == 0) return ret;
  4. int lo = 0, hi = nums.size() - 1;
  5. while(lo < hi)
  6. {
  7. int mid = lo + (hi - lo) / 2;
  8. if(target > nums[mid])
  9. {
  10. lo = mid + 1;
  11. }else{
  12. hi = mid;
  13. }
  14. }
  15. if(nums[lo] != target) return ret;
  16. else ret[0] = lo;
  17. hi = nums.size() - 1;
  18. while(lo < hi)
  19. {
  20. int mid = lo + (hi - lo) / 2 + 1; // important
  21. if(target < nums[mid])
  22. {
  23. hi = mid - 1;
  24. }else{
  25. lo = mid;
  26. }
  27. }
  28. ret[1] = hi;
  29. return ret;
  30. }

Find K Closest Elements

Given a sorted array, two integers k and x, find the k closest elements to x in the array. The result should also be sorted in ascending order. If there is a tie, the smaller elements are always preferred.

Example 1:

Input: [1,2,3,4,5], k=4, x=3
Output: [1,2,3,4]

Example 2:

Input: [1,2,3,4,5], k=4, x=-1
Output: [1,2,3,4]

Note:

先找到x或者比x稍大的元素的位置,然后整理出k个元素,返回。

时间复杂度: 搜索 + 整理k个元素 =
空间复杂度:

  1. vector<int> findClosestElements(vector<int>& arr, int k, int x) {
  2. if(x < arr[0]) return vector<int>(arr.begin(), arr.begin()+k);
  3. if(x > arr[arr.size() - 1]) return vector<int>(arr.end() - k, arr.end());
  4. int lo = 0, hi = arr.size() - 1;
  5. while(lo < hi)
  6. {
  7. int mid = lo + (hi - lo) / 2;
  8. if(arr[mid] == x) { hi = mid; break;}
  9. if(arr[mid] < x) lo = mid + 1;
  10. else hi = mid - 1;
  11. }
  12. // hi指向x或者 >= x 的第一个元素
  13. lo = hi;
  14. hi ++;
  15. // 找出k个元素
  16. while(k)
  17. {
  18. if(hi >= arr.size() || lo >= 0 && x - arr[lo] <= arr[hi] - x)
  19. {
  20. lo --;
  21. }else
  22. {
  23. hi ++;
  24. }
  25. k--;
  26. }
  27. // ↓这里要加1
  28. return vector<int>(arr.begin() + lo + 1, arr.begin() + hi);
  29. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注