【分治】peak find

分治算法

算法設計中一種經常使用的優化方法就是分治的思想,它的解決思路就是將原始的問題劃分爲性質同樣,可是規模減少的子問題,而後經過子問題的解和合並子問題的解獲得最終的解,就是分治的思想;算法

比較常見的分治有 歸併排序算法,快速排序算法,兩種都是優化的排序算法;數組

歸併排序

歸併排序算法經過將問題劃分爲左半部分、右半部分問題的解決,而後經過合併有序的左半部分、有序的右半部分使得最終有序。因此假設問題的求解時間複雜度爲 T(n) = 2*T(n/2) + C(n),其中T(n)表示長度爲n的歸併排序複雜度,等於兩個子問題的複雜度和合並兩個有序序列的複雜度C(n),C(n)表明一個關於n的常數項複雜度,最終時間複雜度nlog(n),優化

快速排序

快速排序的思想和歸併排序的思想比較相似,隨機找到一個partition把數組劃分爲比它小的部分,比它大的部分,而後依次使用這種思想繼續劃分其左半部分,右半部分,可是不能保證每次都可以將問題規模正好的切分爲兩個相同的,因此平均時間複雜度爲nlog(n),最壞的時間複雜度爲n^2(每次的patition元素選擇都很糟糕)設計

切入正題,使用分治的兩個算法題目code

一維數組的局部最大值查找

題目描述:在一個整數數組中查找一個數,該數大於等於其左邊的元素、而且大於等於其右邊的元素(假定數組最邊界是極小值,即-1,n的索引位置爲Integer.MIN_VALUE)
舉例:[1,2,3,4,5]則返回5, [1,3,2,4,3,5]中返回3,4,5任意一個;blog

思路:排序

逐個遍歷能夠查找到全部的局部最大值,可是時間複雜度O(n),題目要求找到一個便可,因此應該有優化的空間。
可以比n更優化的很容易想到二分的思路,因此順着這個思路,首先找到mid元素,若是mid元素知足局部最大值,直接返回,不知足?
如下三種狀況,此時選擇最大的那一半繼續二分查找便可,由於最大的那一邊一定包含一個局部最大值
索引

public int peekFind1(int[] array) {
        int len = array.length;
        //len == 0 exception
        int l = 0, r = len - 1;
        while(r > l) {
            int m = l + (r - l)/2;
            if( (m - 1 < 0 || array[m] >= array[m-1]) && (m + 1 >= len || array[m] >= array[m+1]) ) {
                return array[m];
            }
            else if( (m - 1 >= 0 && array[m-1] > array[m]) ) {
                r = m-1;
            }
            else {
                l = m+1;
            }
        }
        return array[r];
    }

二維數組的局部最大值查找

題目描述:在一個二維整數數組中查找一個數,該數大於等於其上、下、左、右的元素(假定數組最邊界是極小值)it

思路:io

逐個遍歷能夠查找到全部的局部最大值,可是時間複雜度O(n^2),題目要求找到一個便可,因此應該有優化的空間。

二維數組的擴展正常的思路可能會想着藉助一維數組已有的解決方案去解決這個題目,可是這麼想可能就會陷入死衚衕,
好比針對每行使用剛纔一維的解決方案,找到每行的局部最大值,好像並無什麼幫助,
在針對每列使用剛纔一維的解決方案,找到每列的局部最大值,可是沒辦法保證與剛纔每行的有重疊?
因此想利用一維的算法求出每列或者每行的最大值,此時在最大值的覺得數組中使用一維的解決方案便可,可是求解每列或者每行的最大值使得問題複雜度達到O(n^2)了;

因此思路回退到分治的策略,好比先找到中間一列的最大值,這個值一定大於其上下,而後查看這個值左右的狀況,若是知足局部最大值,直接返回,不知足?
一樣是上面的三種狀況,選擇有更大元素的那半邊繼續採用這種思想求解便可獲得答案,時間複雜度nlog(n),

圖解:

public int maxNum(int[] array) {
        int result = array[0];
        int idx = 0;
        for(int i = 1;i < array.length;i++) {
            if(array[i] > result) {
                result = array[i];
                idx = i;
            }
        }
        return idx;
    }
    public int peekFind2(int[][] matrix) {
        int row = matrix.length;
        //row == 0 exception
        int column = matrix[0].length;
        if(row == 1) return peekFind1(matrix[0]);
        if(column == 1) {
            int[] array = new int[row];
            for(int i = 0;i < row;i++) array[i] = matrix[i][0];
            return peekFind1(array);
        }
        int startR = 0, endR = row - 1;
        while(endR > startR) {
            int midR = startR + (endR - startR)/2;
            int maxIdx = maxNum(matrix[midR]);
            if( (midR - 1 < 0 || matrix[midR][maxIdx] >= matrix[midR - 1][maxIdx]) && (midR + 1 >= row || matrix[midR][maxIdx] >= matrix[midR + 1][maxIdx]) ) {
                return matrix[midR][maxIdx];
            }
            else if( (midR - 1 >= 0 && matrix[midR - 1][maxIdx] > matrix[midR][maxIdx]) ) {
                endR = midR - 1;
            }
            else {
                startR = midR + 1;
            }
        }
        return matrix[startR][maxNum(matrix[startR])];
    }

針對二維的局部最大值求解存在O(n)時間複雜度的解決方案,這個後面再分享,思考算法的時候儘量的發散思惟,不能僵化住本身的思想。 若是發現上面有什麼錯誤,歡迎指正。

相關文章
相關標籤/搜索