二分查找的巧妙运用(C++)

来源:互联网 发布:电子音乐制作软件 编辑:程序博客网 时间:2024/05/16 09:08
一、二分查找
1、查找无序数组中的任意一个局部最小值
局部最小值:arr长度为1时,arr[0]是局部最小。arr的长度为N(N>1)时,如果arr[0]<arr[1],那么arr[0]是局部最小;如果arr[N-1]<arr[N-2],那么arr[N-1]是局部最小;如果0<i<N-1,既有arr[i]<arr[i-1]又有arr[i]<arr[i+1],那么arr[i]是局部最小。
解法:二分查找。先查看左右两边是不是局部最小。都不是则局部最小一定在中间,于是查看mid处是不是,若不是则局部最小要么在其左边要么在其右边,判断出在左边还是在右边,继续二分查找即可。
class Solution {
public:
    intgetLessIndex(vector<int> arr) {
        intlength = arr.size();
        if(length == 0)
            return-1;
        if(length == 1)
            return0;
        if(arr[0] < arr[1])
            return0;
        if(arr[length-1] < arr[length-2])
            returnlength-1;
        intstart = 0,end = length-1;
        intmid = (start+end)/2;
        while(1) {
            if(arr[mid] < arr[mid+1] && arr[mid] < arr[mid-1])
                returnmid;
            elseif(arr[mid] > arr[mid-1]) {
                end = mid;
                mid = (start+end)/2;
            }
            else{
                start = mid;
                mid = (start+end)/2;
            }            
        }
    }
};

2、对于一个有序数组arr,再给定一个整数num,请在arr中找到num这个数出现的最左边的位置
解法:二分查找。要注意当arr[mid]大于或等于num时都要转向左半部查找。查找停止条件很重要。
classLeftMostAppearance {
public:
    intfindPos(vector<int> arr, intn, intnum) {
        intres = -1;
        intstart = 0,end = n-1;
        intmid = (start+end)/2;
         
        while(start <= end) {
            if(arr[mid] < num) {
                start = mid+1;               
            }
            elseif(arr[mid] == num) {
                res = mid;
                end = mid-1;
            }
            else{
                end = mid-1;
            }
            mid = (start+end)/2;
        }
        returnres;
    }
};

3、有序循环数组arr,返回arr中的最小值
有序循环数组:1,2,3,4,5      2,3,4,5,1      3,4,5,1,2     4,5,1,2,3      5,1,2,3,4
解法:①判断最左和最右的关系。确定数组是有序的还是循环过的。
           ②判断最左和中间的关系。确定最小值位于左半部还是右半部。
           ③最左大于中间,在左半部,end = mid;最左小于或等于中间,在右半部,但是start的更新不同,小于时        start=mid,等于时start=mid+1.

class MinValue {
public:
    int getMin(vector<int> arr, int n) {
        if (n == 0) {
            return -1;
        }

        int start = 0,end = n-1;
        int mid = start + (end-start)/2;

        if (arr[start] < arr[end]) {
            return arr[start];
        }        
        else if (arr[start] > arr[end]) {
                while (start != end) {
                    if (arr[start] > arr[mid]) {
                        end = mid;
                }
                else if(arr[start] < arr[mid]){
                    start = mid;
                }
                else {
                    start = mid+1;
                }
                mid = start + (end-start)/2;
            }
                return arr[start];            
        }
        else {
            int min = arr[start];           
            for (int i = 0;i < n;i++) {
                if (arr[i] < min)
                    min = arr[i];
            }
            return min;
        }        
    }
};

4、返回最左原位。即对于有序数组arr,其中不含有重复元素,请找到满足arr[i]==i条件的最左的位置
解法:直接判断mid是否满足,arr[mid] >= mid都说明只有左半部有可能出现满足条件的元素,arr[mid] < mid说明只有右半部有可能。
classFind {
public:
    intfindPos(vector<int> arr, intn) {
        intres = -1;
        if(n == 0)
            returnres;
        intstart = 0,end = n-1;
        intmid = -1;
        if(arr[start] == start)
            returnstart;
        while(start <= end) {
            mid = start + (end-start)/2
            if(arr[mid] == mid) {
                res = mid;
                end = mid-1;
            }
            elseif(arr[mid] > mid) 
                end = mid-1;
            else 
                start = mid + 1;
        }
        returnres;
    }
};
5、统计完全二叉树的结点总数
要求:时间复杂度低于O(N)。
解法:二分查找。
①先遍历到二叉树左子树最左边的结点和右子树最左边的结点,统计左右子树的深度。
②若左右子树深度相同,说明左子树是满二叉树,若左子树深度大于右子树深度,说明右子树是满二叉树。
③递归地进行上述过程。
classCountNodes {
public:
    intcount(TreeNode* root) {
        if(!root)
            return0;
        intleftDepth = 0,rightDepth = 0;
        TreeNode *leftL = root,*rightL = root;
        while(leftL || rightL) {           
            if(leftL) {
                leftDepth++;
                leftL = leftL->left;
            }              
            if(rightL) {
                rightDepth++;
                if(rightL == root)
                    rightL = rightL->right;
                else
                    rightL = rightL->left;
            }              
        }
        if(leftDepth == rightDepth) {
            returncount(root->right) + pow(2,leftDepth-1);
        }
        else
            returncount(root->left) + pow(2,rightDepth-1);
    }
};

6、快速幂算法
要求:求整数k的N次方的过程请实现时间复杂度为O(logN)的方法。
解法:N的二进制表示完美地划分了求幂的过程,二进制有多少位基数base就计算多少次,每一次求base都等于上一次的base的平方,将二进制中为1的位对应的基数base乘起来就是最后的结果。
classQuickPower {
public:
    int getPower(int k, int N) {
        long  res=1;
        long  base=k;
        while (N){
            if (N&1)
                res=res*base%1000000007;
            base=base*base%1000000007;
            N>>=1;
        }
        return res;
    }
};

7、矩阵的快速幂
应用:计算斐波那契数列。
http://www.cnblogs.com/vongang/archive/2012/04/01/2429015.html

推荐博文:

二叉树相关练习题(C++)
经典排序算法的C++实现
与字符串有关的一些典型问题的C++解法
一些可以用动态规划(DP)算法解决的问题(C++)
排列组合相关笔试面试题(C++)
与概率相关的算法题C++解法(附证明过程)
位运算在算法题中的使用(C++)
链表相关练习题(C++)
用实例讲解栈和队列(C++)
一些智力题的C++解法

1 0