【LeetCode】二分 binary_search(共58题)
【4】Median of Two Sorted Arrays
【29】Divide Two Integers
【33】Search in Rotated Sorted Array
【34】Find First and Last Position of Element in Sorted Array
【35】Search Insert Position
【50】Pow(x, n)
【69】Sqrt(x)
【74】Search a 2D Matrix (2019年1月25日,谷歌tag复习)剑指offer原题
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:
- Integers in each row are sorted from left to right.
- The first integer of each row is greater than the last integer of the previous row.
Example 1:
Input: matrix = [ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50] ] target = 3 Output: true
Example 2:
Input: matrix = [ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50] ] target = 13 Output: false
题解:用 target 和当前右上角元素做比较,如果相等返回 true,如果不想等,如果右上角元素大于target,那么删除这一列,如果右上角元素小于target,那么删除这一行。
1 class Solution { 2 public: 3 bool searchMatrix(vector<vector<int>>& matrix, int target) { 4 if (matrix.empty() || matrix[0].empty()) { 5 return false; 6 } 7 const int n = matrix.size(), m = matrix[0].size(); 8 int up = 0, right = m-1; 9 while (up < n && right >= 0) { 10 if (matrix[up][right] == target) { 11 return true; 12 } 13 if (matrix[up][right] < target) { 14 up++; 15 } else if (matrix[up][right] > target) { 16 --right; 17 } 18 } 19 return false; 20 } 21 };
【81】Search in Rotated Sorted Array II
【153】Find Minimum in Rotated Sorted Array
【154】Find Minimum in Rotated Sorted Array II
【162】Find Peak Element (2018年11月27日)(本题需要复习,一开始不会做的。我觉得二分也容易写错的。)
这题要求我们在一个无序的数组里找到一个peak元素,所谓peak,就是值比两边邻居大就可以了。
题解:对于这道题目,最简单的解法就是遍历数组,只要找到第一个符合要求的元素就可以了,时间复杂度为O(n),但是这题要求O(LogN)的时间复杂度,还可以用二分来做。https://blog.csdn.net/NK_test/article/details/49926229
首先我们找到中间节点mid,如果大于两边返回当前的index就可以了,如果左边的节点比mid大,那么我们可以继续在左半区间查找,这里面一定存在一个peak,为什么这么说呢?假设此时的区间范围为[0,mid-1],因为num[mid-1]一定大于num[mid],如果num[mid-2]<=num[mid-1],那么num[mid-1]就是一个peak。如果num[mid-2]>num[mid-1],那么我们就继续在[0,mid-2]区间查找,因为num[-1]为负无穷,所以我们最终绝对能在左半区间找到一个peak。同理右半区间一样。
1 class Solution { 2 public: 3 int findPeakElement(vector<int>& nums) { 4 const int n = nums.size(); 5 int left = 0, right = n - 1; 6 while (left < right) { 7 int mid = (left + right) / 2; 8 int target = nums[mid+1]; 9 if (nums[mid] < target) { 10 left = mid + 1; 11 } else { 12 right = mid; 13 } 14 } 15 return left; 16 } 17 };
2019年4月15日更新。我们可以根据mid和旁边元素的大小来移动指针。我们的目标其实是把搜索方向放到元素增大的一侧。
如果我们把二分写成如下的形式,那么可以发现,当 left = right 的时候退出循环,mid 永远取不到 right,那么 mid + 1 也就永远合法。
1 class Solution { 2 public: 3 int findPeakElement(vector<int>& nums) { 4 const int n = nums.size(); 5 int left = 0, right = n-1; 6 while (left < right) { 7 int mid = (left + right) / 2; 8 if (nums[mid] < nums[mid+1]) { 9 left = mid + 1; 10 } else { 11 right = mid; 12 } 13 } 14 return left; 15 } 16 };
【167】Two Sum II - Input array is sorted
【174】Dungeon Game
【209】Minimum Size Subarray Sum
【222】Count Complete Tree Nodes
【230】Kth Smallest Element in a BST
【240】Search a 2D Matrix II (2019年1月26日,谷歌tag复习)
write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:
- Integers in each row are sorted in ascending from left to right.
- Integers in each column are sorted in ascending from top to bottom.
题解:很多种方法可以做,我还是每次看右上角元素。
1 class Solution { 2 public: 3 bool searchMatrix(vector<vector<int>>& matrix, int target) { 4 if (matrix.empty() || matrix[0].empty()) { 5 return false; 6 } 7 const int n = matrix.size(), m = matrix[0].size(); 8 int up = 0, right = m-1; 9 while (up < n && right >= 0) { 10 if (matrix[up][right] == target) { 11 return true; 12 } 13 while (up < n && matrix[up][right] < target) { 14 ++up; 15 } 16 if (up == n) { break; } 17 while (right >= 0 && matrix[up][right] > target) { 18 --right; 19 } 20 } 21 return false; 22 } 23 };
【270】Closest Binary Search Tree Value ()
【275】H-Index II
【278】First Bad Version (2018年12月22日,地里面经)
给了一个数字 n, 代表数组 [1..n],给了一个 api, bool isBadVersion(int version); 能判断一个数字是不是 bad version。在调用这个给定的api最小次数的前提下,返回这个数组中第一个bad version。
题解:二分,lower_bound 自己实现
1 // Forward declaration of isBadVersion API. 2 bool isBadVersion(int version); 3 4 class Solution { 5 public: 6 int firstBadVersion(int n) { 7 long long left = 0, right = (long long)n + 1; 8 long long mid; 9 while (left < right) { 10 mid = left + (right - left) / 2; 11 if (!isBadVersion(mid)) { 12 left = mid + 1; 13 } else { 14 right = mid; 15 } 16 } 17 return left; 18 } 19 };
【287】Find the Duplicate Number (2019年1月26日,二分查找)
给了一个nums数组,里面包含 1 — n-1 的数字,有一个数字可能重复了2次到多次。找出来这个数。
题解:解法1. sort + 2 pointers
1 class Solution { 2 public: 3 int findDuplicate(vector<int>& nums) { 4 sort(nums.begin(), nums.end()); 5 for (int i = 0; i < nums.size() - 1; ++i) { 6 if (nums[i] == nums[i+1]) { 7 return nums[i]; 8 } 9 } 10 return -1; 11 } 12 };
解法2. 二分==总写错啊
【300】Longest Increasing Subsequence
【302】Smallest Rectangle Enclosing Black Pixels (2019年1月26日,谷歌tag题)
给了一个矩阵,0代表白色像素,1代表黑色像素,黑色所有的像素是四联通的,问把黑色所有像素的包围起来的最小矩形面积。
题解:这题是个二分 tag,但是我只会用dfs解法。dfs解法时间复杂度是O(mn)的。我们需要找四个值,1像素的最左,最右,最上和最下。然后用矩阵的长和宽相乘一下就可以了。
1 class Solution { 2 public: 3 int minArea(vector<vector<char>>& image, int x, int y) { 4 if (image.size() == 0 || image[0].size() == 0) { 5 return 0; 6 } 7 n = image.size(), m = image[0].size(); 8 vector<vector<int>> visit(n, vector<int>(m, 0)); 9 left = right = y; 10 top = buttom = x; 11 dfs(image, x, y, visit); 12 int area = (right - left + 1) * (buttom - top + 1); 13 return area; 14 } 15 int n, m; 16 int left = -1, right = -1, top = -1, buttom = -1; 17 void dfs(const vector<vector<char>>& image, int x, int y, vector<vector<int>>& visit) { 18 visit[x][y] = 1; 19 left = min(left, y), right = max(right, y); 20 top = min(top, x), buttom = max(buttom, x); 21 for (int k = 0; k < 4; ++k) { 22 int newx = x + dirx[k], newy = y + diry[k]; 23 if (newx >= 0 && newx < n && newy >= 0 && newy < m && image[newx][newy] == '1' && !visit[newx][newy]) { 24 dfs(image, newx, newy, visit); 25 } 26 } 27 return; 28 } 29 int dirx[4] = {-1, 0, 1, 0}; 30 int diry[4] = {0, -1, 0, 1}; 31 };
【349】Intersection of Two Arrays (2018年11月6日,算法群相关题)
hash-table 里面有这题,hash-table:https://www.cnblogs.com/zhangwanying/p/9886262.html
也可以二分解答,二分没有想过,我估计就是先排序,然后二分吧
【350】Intersection of Two Arrays II (2018年11月6日,算法群)
hash-table 里面有这题,hash-table:https://www.cnblogs.com/zhangwanying/p/9886262.html
也可以二分解答,二分没有想过,我估计就是先排序,然后二分吧
【354】Russian Doll Envelopes
【363】Max Sum of Rectangle No Larger Than K
【367】Valid Perfect Square
【374】Guess Number Higher or Lower (2019年1月25日,谷歌tag复习)
在 [1, n] 这个区间里面猜数,给了一个 guess 的api,返回一开始 pick 的数字。
You call a pre-defined API guess(int num)
which returns 3 possible results (-1
, 1
, or 0
):
-1 : My number is lower 1 : My number is higher 0 : Congrats! You got it!
题解:二分,这题我写成了左闭右闭的形式。
1 // Forward declaration of guess API. 2 // @param num, your guess 3 // @return -1 if my number is lower, 1 if my number is higher, otherwise return 0 4 int guess(int num); 5 6 class Solution { 7 public: 8 int guessNumber(int n) { 9 int left = 1, right = n; 10 long long mid; 11 while (left <= right) { 12 mid = (long long)left + (right - left) / 2; 13 int res = guess(mid); 14 if (res == 0) { 15 return mid; 16 } else if (res < 0) { //leftside 17 right = mid - 1; 18 } else { 19 left = mid + 1; 20 } 21 } 22 return -1; 23 } 24 };
【378】Kth Smallest Element in a Sorted Matrix (2019年2月9日)
给了一个 n * n 的矩阵,返回矩阵中第 k 小的元素。
题解:二分答案。我们要找到一个最小的元素x,满足矩阵中的元素小于等于x的值的有k个。(lower_bound)
1 class Solution { 2 public: 3 int kthSmallest(vector<vector<int>>& matrix, int k) { 4 const int n = matrix.size(); 5 int left = matrix[0][0], right = matrix[n-1][n-1] + 1; 6 while (left < right) { 7 int mid = left + ((right - left) / 2); 8 int tot = 0; 9 for (auto& row : matrix) { 10 auto iter = upper_bound(row.begin(), row.end(), mid); 11 tot += distance(row.begin(), iter); 12 } 13 // printf("left = %d, right = %d, mid = %d, tot = %d\n", left, right, mid, tot); 14 if (tot < k) { 15 left = mid + 1; 16 } else { 17 right = mid; 18 } 19 } 20 return left; 21 } 22 };
【392】Is Subsequence
【410】Split Array Largest Sum (2019年3月2日,谷歌tag)
给了一个连续的数组,每个元素代表任务完成的时间,然后给了一个天数m,要求这些任务必须在m天之内完成(可以提前)但是这些任务必须按顺序做,求最小化这这些天花在任务上时间的最大值。
举个例子:
nums = [7,2,5,10,8] m = 2
Output: 18 Explanation: There are four ways to split nums into two subarrays. The best way is to split it into [7,2,5] and [10,8], where the largest sum among the two subarrays is only 18.
题解:我们其实可以枚举这个时间,我们假设一天最多做 k 个小时,看 m 天之内能不能完成。如果不能的话,我们尝试扩大k,如果可以的话,我们尝试缩小k。所以二分查找的思路就出来了。left 边界是数组的最大值,right边界是整个数组的和。
整体时间复杂度是 O(nlogn)
1 class Solution { 2 public: 3 int splitArray(vector<int>& nums, int m) { 4 const int n = nums.size(); 5 long long left = -1, right = 0; 6 for (auto& num : nums) { 7 right += num; 8 if (num > left) { 9 left = num; 10 } 11 } 12 int res(-1); 13 while (left <= right) { 14 long long mid = left + (right - left) / 2; 15 if (check(nums, mid, m)) { 16 res = mid; 17 right = mid - 1; 18 } else { 19 left = mid + 1; 20 } 21 } 22 return res; 23 } 24 bool check(vector<int>& nums, long long mid, int m) { 25 int cnt = 0; long long sum = 0LL; 26 for (auto num : nums) { 27 if (sum + num <= mid) { 28 sum += num; 29 } else { 30 cnt++; 31 sum = num; 32 if (cnt == m || sum > mid) {return false;} 33 } 34 } 35 return true; 36 } 37 };
【436】Find Right Interval
【441】Arranging Coins (2018年11月26日)
给了 n 枚硬币, 我们排列这些硬币,第一行放1个,第二行放2个,.. ,第 k 行放 k 个。问这 n 个硬币最多能完全放满多少行。
题解:我一个解法是用 等差数列的公式求解的, k * (k + 1) <= 2 * n。 枚举 k, 找到最大满足条件的 k,然后 返回 k . 这个解法只能 beats 20%+。
后来我看是二分的tag,我就写了一个 二分,然后就beats 90+了。
1 class Solution { 2 public: 3 int arrangeCoins(int n) { 4 int k = my_upper_bound(1, (long long)n + 1, (long long)n * 2); 5 return k - 1; 6 } 7 int my_upper_bound(int begin, long long end, long long target) { 8 long long mid = 0; 9 while (begin < end) { 10 mid = ((long long)begin + end) / 2; 11 long long temp = mid * (mid + 1); 12 if (temp > target) { 13 end = mid; 14 } else { 15 begin = mid + 1; 16 } 17 } 18 return begin; 19 } 20 };
【454】4Sum II
【475】Heaters (2019年2月26日)
给了一个房子数组和一个暖气数组,求暖气的最小半径,要求所有的房子必须被暖气覆盖。
题解:对于每一个房子求离它最近的暖气,用 lower_bound 求,然后用这个半径和 global_max 做比较。时间复杂度是 O(nlogn)
1 class Solution { 2 public: 3 int findRadius(vector<int>& houses, vector<int>& heaters) { 4 sort(heaters.begin(), heaters.end()); 5 int res = 0; 6 for (auto& pos : houses) { 7 auto iter = lower_bound(heaters.begin(), heaters.end(), pos); 8 int radius = 0; 9 if (iter == heaters.begin()) { 10 radius = *iter - pos; 11 } else if (iter == heaters.end()) { 12 --iter; 13 radius = pos - *iter; 14 } else { 15 radius = min(*iter - pos, pos - *(iter-1)); 16 } 17 res = max(res, radius); 18 } 19 return res; 20 } 21 };
【483】Smallest Good Base
【497】Random Point in Non-overlapping Rectangles
【528】Random Pick with Weight
【644】Maximum Average Subarray II
【658】Find K Closest Elements
【668】Kth Smallest Number in Multiplication Table
【702】Search in a Sorted Array of Unknown Size (2019年1月26日,二分查找复习,谷歌tag,lower_bound思想, M)
给了一个不知道长度的array,问target是否存在在array中。ArrayReader.get(k) 能获取 index = k的值。数据范围:
You may assume all integers in the array are less than 10000
, and if you access the array out of bounds, ArrayReader.get
will return 2147483647
.
- You may assume that all elements in the array are unique.
- The value of each element in the array will be in the range
[-9999, 9999]
.
题解:我推算出数组最长为 20000,所以left = 0, right = 20000,直接用lower_bound, 如果说 reader.get(mid) 这个数字是 INT_MAX 的话,right = mid,丢弃所有右边的。如果说 reader.get(mid) < target 的话,那么说明,mid 这个依旧不满足条件,我们需要把整个左边和mid一起丢掉 left = mid + 1。剩下的话,就是 right = mid
1 // Forward declaration of ArrayReader class. 2 class ArrayReader; 3 4 class Solution { 5 public: 6 int search(const ArrayReader& reader, int target) { 7 long long left = 0, right = 20000, mid = 0; 8 while (left < right) { 9 mid = (left + right) / 2; 10 int temp = reader.get(mid); 11 if (temp == INT_MAX) { 12 right = mid; 13 } else if (temp < target) { 14 left = mid + 1; 15 } else { 16 right = mid; 17 } 18 } 19 if (reader.get(left) == target) { 20 return left; 21 } 22 return -1; 23 } 24 };
【704】Binary Search
【710】Random Pick with Blacklist
【718】Maximum Length of Repeated Subarray
【719】Find K-th Smallest Pair Distance
【744】Find Smallest Letter Greater Than Target
【774】Minimize Max Distance to Gas Station
【778】Swim in Rising Water
【786】K-th Smallest Prime Fraction
【793】Preimage Size of Factorial Zeroes Function
【852】Peak Index in a Mountain Array (2019年2月27日,google tag)
给了一个三角顺序的数组,(前半段递增,后半段递减),返回值最大的元素下标。
Example 1: Input: [0,1,0] Output: 1 Example 2: Input: [0,2,1,0] Output: 1
题解:二分,如果遍历一遍就行的话,这就不应该是一个面试题。时间复杂度是 O(logN)
如果 nums[mid] < nums[mid+1], 说明mid还在递增的区间,我们这个时候应该++left。
不然如果 nums[mid-1] < nums[mid] > nums[mid+1],说明mid已经是我们寻找的目标值。
再或者 nums[mid-1] > nums[mid], 说明现在已经在递减的区间。我们应该 right = mid
1 class Solution { 2 public: 3 int peakIndexInMountainArray(vector<int>& A) { 4 const int n = A.size(); 5 int left = 0, right = n; 6 while (left < right) { 7 int mid = (left + right) / 2; 8 if (mid + 1 < right && A[mid] < A[mid+1]) { 9 left = mid + 1; 10 } else if (mid - 1 >= left && A[mid-1] < A[mid]) { 11 return mid; 12 } else { 13 right = mid; 14 } 15 } 16 return left; 17 } 18 };
【862】Shortest Subarray with Sum at Least K
【875】Koko Eating Bananas
【878】Nth Magical Number
【887】Super Egg Drop