• 二分查找题目汇总


    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;
        }
    };
    

      

  • 相关阅读:
    Hive-0.12.0 配置Mysql的Metasotre
    cdecl、stdcall、fastcall、thiscall函数调用约定区别 (转)
    汇编函数 哪些寄存器在使用时需要保护和恢复现场
    如何用C++ 写Python模块扩展(二)
    如何用C++ 写Python模块扩展(一)
    python装饰器装饰原理探秘
    Linux 命令
    iOS为所需要的视图添加模糊效果--UIVisualEffectView
    UIAlertView ----警告视图
    Virtual Box 下Ubuntu桥接网络设置
  • 原文地址:https://www.cnblogs.com/Allen-rg/p/13589882.html
Copyright © 2020-2023  润新知