二分查找题目汇总

https://blog.csdn.net/luoshengkim/article/details/52103427

 

457. Classical Binary Search

这是一道非常经典的二分查找题,给出一个有序数组以及一个目标值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;
    }
};

  

 

posted @ 2020-08-31 15:45  静悟生慧  阅读(596)  评论(0编辑  收藏  举报