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

一、二分查找
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、矩陣的快速冪
應用:計算斐波那契數列。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章