找出数组中的峰值

这种题目分为两种类型,

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) 。

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值