二分查找题目汇总
https://blog.csdn.net/luoshengkim/article/details/52103427
这是一道非常经典的二分查找题,给出一个有序数组以及一个目标值target,要求返回target在数组中的位置,若数组里不存在target,则返回-1。套用经典的二分查找模板即可:
//给出一个有序数组以及一个目标值target,要求返回target在数组中的位置,若数组里不存在target,则返回-1 class Solution { public: /** * @param nums: An integer array sorted in ascending order * @param target: An integer * @return: An integer */ int findPosition(vector<int> &nums, int target) { // write your code here if (nums.size()== 0) { return -1; } int left = 0; int right = nums.size() -1; while(left<=right){ int mid = left + (right-left)/2; if(nums[mid]==target){ return mid; }else if(nums[mid] < target){ left = mid+1; }else if(nums[mid] > target){ right = mid -1; } } return -1; } }; // 给定一个有序数组和一个目标值,要求在O(logn)的时间内找到那个目标值第一个出现的位置(数组中可能存在多个相同的目标值) class Solution { public: /** * @param nums: The integer array. * @param target: Target to find. * @return: The first position of target. Position starts from 0. */ int binarySearch(vector<int> &nums, int target) { // write your code here if (nums.size()== 0){ return -1; } int left = 0; int right = nums.size() -1; while(left<=right){ int mid = left + (right-left)/2; if(nums[mid]==target){ right = mid-1; }else if(nums[mid] > target){ right = mid-1; }else if(nums[mid] < target){ left = mid+1; } } //没有找到或者超出边界,返回-1 if (left >= nums.size() || nums[left] != target){ return -1; } // 返回left 时,left已经是大于right的值,如果取小于的话,最终的返回值应该为left-1 return left; } }; // 给定一个有序数组和一个目标值,要求在O(logn)的时间内找到那个目标值最后1个出现的位置(数组中可能存在多个相同的目标值) class Solution { public: /** * @param nums: An integer array sorted in ascending order * @param target: An integer * @return: An integer */ int lastPosition(vector<int> &nums, int target) { // write your code here if (nums.size()== 0){ return -1; } int left = 0; int right = nums.size() -1; while(left<=right){ int mid = left + (right-left)/2; if(nums[mid]==target){ left = mid+1; }else if(nums[mid] > target){ right = mid-1; }else if(nums[mid] < target){ left = mid+1; } } //没有找到或者超出边界,返回-1 if (right < 0 || nums[right] != target){ return -1; } // 返回right 或者返回 left-1 return right; } }; // 在一个有序数组中,给定一个target,要求在数组中找到离target最近的数。 // Given [1, 4, 6] and target = 3, return 1. // Given [1, 4, 6] and target = 5, return 1 or 2. // Given [1, 3, 3, 4] and target = 2, return 0 or 1 or 2. // 给定一个排序数组和一个目标值,如果在数组中找到目标值则返回索引。如果没有,返回到它将会被按顺序插入的位置。你可以假设在数组中无重复元素。 [1,3,5,6],5 → 2 [1,3,5,6],2 → 1 [1,3,5,6],7 → 4 [1,3,5,6],0 → 0 class Solution { public: /** * @param A: an integer sorted array * @param target: an integer to be inserted * @return: An integer */ int searchInsert(vector<int> &A, int target) { // write your code here if(A.size() == 0){ return 0; } int left = 0; int right = A.size()-1; // 找到对应的数据则返回,找不到则再进行判断插入位置 while(left<= right){ int mid = left +(right-left)/2; if(A[mid]==target){ return mid; }else if(A[mid]<target){ left = mid+1; }else if(A[mid]>target){ right = mid - 1; } } if(A[right]<target){ return right+1; } if(A[right]>target){ return right; } return 0; } };
// 实现 int sqrt(int x) 函数,计算并返回 x 的平方根。 // 样例 // 样例 1: // 输入: 0 // 输出: 0 // 样例 2: // 输入: 3 // 输出: 1 // 样例解释: // 返回对x开根号后向下取整的结果。 // 样例 3: // 输入: 4 // 输出: 2 class Solution { public: /** * @param x: An integer * @return: The sqrt of x */ int sqrt(int x) { // write your code here int left = 0; int right = x; while (left<=right){ long mid = left + (right-left)/2; if(mid*mid == x){ return mid; }else if (mid*mid > x){ right = mid -1; }else if (mid*mid < x){ left = mid +1; } } if (right*right < x){ return right; } if (right*right > x) { return right-1; } } }; //要求出一个数的开根号,只不过从整数换成了小数。当然小数是没办法精确地计算根号值的,只能近似的去模拟,比如误差不大于多少1e-10。 // 两种方案,一种用牛顿法,一种用二分查找法 #include <iostream> #include <cmath> using namespace std; // err 是允许的误差 const double err = 1e-8; double NtSqrt(const double num) { if (num < 0) { return -1; } else { double root = num; // 如果原值减去近似根的平方大于误差,继续循环 while (abs(num - root * root) >= err) { // 得到下一个近似根 root = (num / root + root) / 2.0; } return root; } } int main() { double num; cout << "请输入一个数: "; cin >> num; double ans = NtSqrt(num); if (ans == -1) { cout << "负数没有平方根" << endl; } else { cout << num << " 的平方根是 " << ans << endl; } return 0; } // 二分查找法 class Solution { public: /** * @param x: a double * @return: the square root of x */ double sqrt(double x) { double result; double start = 0, end, mid; double origX = x; if (x < 1) x = 1.0 / x; end = x; while(start + 1e-9 < end) { mid = start + (end - start) / 2; if (mid * mid < x) { start = mid; } else { end = mid; } } result = start + (end - start) / 2; if (origX < 1) { result = 1.0 / result; } return result; } }; <!-- 要自己实现一个求幂函数。 最直观容易想到的方法就是用递归方法求n个x的乘积,注意考虑n的正负号,时间复杂度为O(n) --> // 先注意特殊情况,比如底数为0,或者小于0的情况,然后转换成一般情况 class Solution { public: /** * @param x: the base number * @param n: the power number * @return: the result */ double myPow(double x, int n) { // write your code here if(n == 0){ return 1; } // 针对特殊溢出情况,加的补丁 if((n<=INT_MIN || n>=INT_MAX) && (x>1 || x<-1)) return 0; if(x==1 && n==INT_MIN) return 1; if(n<0){ return 1.0/myPow(x,-n); } double half = myPow(x,n/2); if (n%2==0){ return half*half; } if (n%2==1){ return x*half*half; } } }; <!-- 代码库的版本号是从 1 到 n 的整数。某一天,有人提交了错误版本的代码,因此造成自身及之后版本的代码在单元测试中均出错。请找出第一个错误的版本号。 你可以通过 isBadVersion 的接口来判断版本号 version 是否在单元测试中出错,具体接口详情和调用方法请见代码的注释部分。 样例 n = 5: isBadVersion(3) -> false isBadVersion(5) -> true isBadVersion(4) -> true 因此可以确定第四个版本是第一个错误版本。 挑战 调用 isBadVersion 的次数越少越好 --> /** * class SVNRepo { * public: * static bool isBadVersion(int k); * } * you can use SVNRepo::isBadVersion(k) to judge whether * the kth code version is bad or not. */ class Solution { public: /** * @param n: An integer * @return: An integer which is the first bad version. */ int findFirstBadVersion(int n) { // write your code here int left =1; int right = n; if (n==1){ return 1; } while(left <= right){ int mid = left + (right -left)/2; if(SVNRepo::isBadVersion(mid)){ right = mid-1; }else if (!SVNRepo::isBadVersion(mid)){ left = mid +1; } } return left; } }; // 样例 // 例1: // 输入: nums1 = [1, 2, 2, 1], nums2 = [2, 2], // 输出: [2]. // 例2: // 输入: nums1 = [1, 2], nums2 = [2], // 输出: [2]. // 挑战 // 可以用三种不同的方法实现吗? // 注意事项 // 结果中的每个元素必须是唯一的。 // 结果需要为升序。 // 解法一:用HashSet,扫描第一个数组,加进HashSet1中,得到的HashSet1是唯一的。然后扫描第二个数组, // 如果第二个数组的元素在HashSet1中存在,则加进HashSet2中。最后得到的HashSet2就是答案了。 // 解法二:先排序,然后扫描第二个数组,在扫描第二个数组的过程中使用binarySearch, // binarySearch即在第一个数组中找第二个数组的某个元素,如果找到了则加入HashSet,这样能保证答案是唯一的。 class Solution { public: /** * @param nums1: an integer array * @param nums2: an integer array * @return: an integer array */ vector<int> intersection(vector<int> &nums1, vector<int> &nums2) { // write your code here set<int> setnums1(nums1.begin(),nums1.end()); set<int> reset; for (int i = 0; i < nums2.size(); i++) { /* code */ //如果某个数在数组1中出现过,但还没有被记录到交集结果数组中,则插入结果数组,set插入默认排序 if(setnums1.count(nums2[i])&&!reset.count(nums2[i])) reset.insert(nums2[i]); } return vector<int>(reset.begin(),reset.end()); } }; <!-- 样例1 输入: nums1 = [1, 2, 2, 1], nums2 = [2, 2] 输出: [2, 2] 样例2 输入: nums1 = [1, 1, 2], nums2 = [1] 输出: [1] --> class Solution { public: /** * @param nums1: an integer array * @param nums2: an integer array * @return: an integer array */ vector<int> intersection(vector<int> &nums1, vector<int> &nums2) { int M = nums1.size(); int N = nums2.size(); if (M == 0 || N == 0) return vector<int>(); // 判断数组长度 vector<int> & large = (M >= N) ? nums1 : nums2; vector<int> & small = (M < N) ? nums1 : nums2; unordered_map<int, int> umLarge; //(number, freq) // 遍历长数组,记录出现次数 for (int i = 0; i < large.size(); ++i) { if (umLarge.find(large[i]) == umLarge.end()) { umLarge[large[i]] = 1; } else { umLarge[large[i]]++; } } vector<int> result; //遍历短数组,并依照map计数,存储交集结果 for (int i = 0; i < small.size(); ++i) { if (umLarge.find(small[i]) != umLarge.end() && umLarge[small[i]] > 0) { result.push_back(small[i]); umLarge[small[i]]--; } } return result; } };
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
- 每行中的整数从左到右按升序排列。
- 每行的第一个整数大于前一行的最后一个整数。
// 要求在一个二维数组中找到某个数target。这个矩阵具有以下特性:每行中的整数从左到右是排序的。每行的第一个数大于上一行的最后一个整数。 // 将二维数组看作有序一维数组,二分查找即可 class Solution { public: bool searchMatrix(vector<vector<int>>& matrix, int target) { if(matrix.empty()) return false; int size_row = matrix.size(); //获取行数 int size_col = matrix[0].size(); //获取列数 int left = 0; int right = size_row*size_col -1; while(left<=right){ int mid = left + (right-left)/2; if(matrix[mid/size_col][mid%size_col]==target){ return true; }else if(matrix[mid/size_col][mid%size_col]>target){ right = mid -1; }else if(matrix[mid/size_col][mid%size_col]<target){ left = mid +1; } } return false; } };
240、搜索二维矩阵II
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
class Solution { public: bool searchMatrix(vector<vector<int>>& matrix, int target) { if (matrix.size() == 0 || matrix[0].size() == 0) return false; const int M = matrix.size(), N = matrix[0].size(); int i = M - 1, j = 0; while (i >= 0 && j < N) { if (matrix[i][j] == target) { return true; } else if (matrix[i][j] < target) { ++j; } else { --i; } } return false; } };