找出数组中的峰值

来源:互联网 发布:嘉兴学院雅尔网络课程 编辑:程序博客网 时间:2024/05/17 09:19

这种题目分为两种类型,

1. 找一维数组的峰值

你给出一个整数数组(size为n),其具有以下特点:

  • 相邻位置的数字是不同的
  • A[0] < A[1] 并且 A[n - 2] > A[n - 1]

假定P是峰值的位置则满足A[P] > A[P-1]A[P] > A[P+1],返回数组中任意一个峰值的位置。

找一维数组的峰值,最暴力的办法是for循环,逐个元素比较左右两边的值,时间复杂度O(N),.

另外其实最优的算法是二分法,O(log N)

class Solution {public:    /**     * @param A: An integers array.     * @return: return any of peek positions.     */    // 1. 第一类面试者 只会 for 循环    int findPeak1(vector<int> A) {        // write your code here        for(int i = 1; i < A.size()-1; i++){            if(A[i] > A[i-1] && A[i] > A[i+1]){                return i;            }        }        return -1;    }    // 2. 第二类面试者 知道二分法    int findPeak2(vector<int> A) {        if(A.size() < 3){            return -1;        }        int start = 1;        int end = A.size()-1;        while(start <= end){            int middle = start + (end - start) / 2;            if(A[middle] < A[middle-1]){                end = middle-1;            }            else if(A[middle] < A[middle+1]){                start = middle+1;            }            else {                return middle;            }        }        return -1;//should never happen    }    // 3. 第二类面试者 知道二分法(模板解法)    int findPeak(vector<int> A) {        if(A.size() < 3){            return -1;        }        int start = 1;        int end = A.size()-2;        while(start+1 < end){            int middle = start + (end - start) / 2;            if(A[middle] < A[middle-1]){                end = middle;            }            else if(A[middle] < A[middle+1]){                start = middle;            }            else {                start = middle;            }        }        if(A[start] > A[end]){            return start;        }        return end;    }};



2. 找二维数组的峰值

一个整数矩阵有如下一些特性:

  • 相邻的整数都是不同的
  • 矩阵有 n 行 m 列。
  • 对于所有的 i < m, 都有 A[0][i] < A[1][i] && A[n - 2][i] > A[n - 1][i].
  • 对于所有的 j < n, 都有 A[j][0] < A[j][1] && A[j][m - 2] > A[j][m - 1].

我们定义一个位置 P 是一个峰,如果有 A[j][i] > A[j+1][i] && A[j][i] > A[j-1][i] && A[j][i] > A[j][i+1] && A[j][i] > A[j][i-1]。

找出该矩阵的一个峰值元素,返回他的坐标

解题思路仍然是二分,可以行二分,也可以列二分,这样时间复杂度O(N log N), 参见下面的解法5

最优的方法是行列同时二分 O(N)

class Solution {    /**     * @param A: An integer matrix     * @return: The index of the peak     */    // 解法4, 第三类面试者 只会O(N * M)的解法    public List<Integer> findPeakII3(int[][] A) {        // write your code here        List<Integer> ans = new ArrayList<Integer>();        int n = A.length;        int m = A[0].length;        for(int i = 1; i < n-1; i++){            for(int j = 1; j < m-1; j++){                int[] dx = {-1, 1, 0, 0};                int[] dy = {0, 0, -1, 1};                if( A[i][j] > A[i+dx[0]][j+dy[0]] &&                    A[i][j] > A[i+dx[1]][j+dy[1]] &&                    A[i][j] > A[i+dx[2]][j+dy[2]] &&                    A[i][j] > A[i+dx[3]][j+dy[3]]){                    ans.add(i);                    ans.add(j);                    return ans;                }            }        }        return ans;    }    // 解法5, 第四类面试者 会优化,会O(N log N)的解法    public List<Integer> findPeakII4(int[][] A) {        List<Integer> ans = new ArrayList<Integer>();        int n = A.length;        int m = A[0].length;        if(n < 3){            return ans; // argument check        }        int start = 1, end = n-2;        while(start <= end){            int row = start + (end - start) / 2;            int col = findLargest(A, row);            if(A[row][col] < A[row-1][col]){                end = row - 1;            }            else if(A[row][col] < A[row+1][col]){                start = row + 1;            }            else{                ans.add(row);                ans.add(col);                return ans;            }        }        return ans;    }    // find largest in a row, return col index    public int findLargest(int [][] A, int row){        int col = 0;        for(int j = 0; j < A[0].length; j++){            if(A[row][j] > A[row][col]){                col = j;            }        }        return col;    }        // 解法六, 第五类面试者 会举一反四,会O(N)的解法     public List<Integer> findPeakII(int[][] A) {        List<Integer> ans = new ArrayList<Integer>();        int n = A.length;        int m = A[0].length;        if(n < 3 || m < 3){            return ans; // argument check        }        int start_row = 1, end_row = n-2;        int start_col = 1, end_col = m-2;        int row = 0, col = 0; // store middle        while(start_row <= end_row && start_col <= end_col){            // search row            row = start_row + (end_row - start_row) / 2;            col = findLargestInRow(A, row, start_col, end_col);            if(A[row][col] < A[row-1][col]){                end_row = row - 1;            }            else if(A[row][col] < A[row+1][col]){                start_row = row + 1;            }            else{                ans.add(row);                ans.add(col);                return ans;            }                        // search column            col = start_col + (end_col - start_col) / 2;            row = findLargestInCol(A, col, start_row, end_row);            if(A[row][col] < A[row][col-1]){                end_col = col - 1;            }            else if(A[row][col] < A[row][col+1]){                start_col = col + 1;            }            else{                ans.add(row);                ans.add(col);                return ans;            }        }        return ans;    }    // search larget one in a row, return col index    public int findLargestInRow(int [][] A, int row, int start_col, int end_col){        int col = start_col;        for(int j = start_col; j <= end_col; j++){            if(A[row][j] > A[row][col]){                col = j;            }        }        return col;    }    // search largest one in a col, return row index;    public int findLargestInCol(int [][] A, int col, int start_row, int end_row){        int row = start_row;        for(int i = start_row; i <= end_row; i++){            if(A[i][col] > A[row][col]){                row = i;            }        }        return row;    }}

最后一种解法是最优的解法,行和列同时二分,同样是利用了一维数组找峰值的思想。时间复杂度是O(N), 为什么呢,下面给出证明:

二分过程中要对行二分扫描中间这一行的最大值,每一行N个数, 然后对列二分找中间一列的最大值,这时候由于之前行二分过,所以每一列只有n/2(或相同数量级)个数,然后再找出(N/2)小矩阵的峰值,这是相同的子问题。于是
T(N)   = N + N/2 + T(N/2) = 3/2  * N + O(N)
T(N/2) = 3/2 * (N/2) + O(N/4)
T(N/4) = 3/2 * (N/4) + O(N/8)
T(N/k) = 3/2 * (N/k) + O(1)
以此类推,所有等式相加起来,抵消得到 T(N) = 3/2 * 2N + O(1) = O(N) 。

0 0
原创粉丝点击