一道面试题(单调栈)

不含负数,可能含有相同数值的山峰数组(环形)
山峰A和山峰B能互相看见的条件是:
1、AB是不同的山且相邻
2、AB是不同的山,且A到B的两个方向上至少有一个方向上没有比A高的山峰
 
  分析:本题有两种情况
      第一种是山峰都是不同高度的,这样的话本题就可以有O(1)的解,山峰如果是不同高度,那么当山峰数量大于等于2的时候,必有一个最高山峰,一个次高山峰
  所以,假设山峰数量为n,则剩下的每个山峰都可以在顺时针方向找到第一个高于它的山峰,逆时针找到第一个高于它的山峰,所以剩下的山峰对就是2 * (n - 2),最后我们要算上次高和最高山峰组成的一对
  那么总共山峰对就是2 * n - 3对。
      第二种情况就是下面代码所示,可以有重复高度的山峰,我们沿着一个方向遍历山峰,维护一个单调栈,从栈低到栈顶为高度上升的,注意这里最重要的一点是,首先遍历山峰高度数组,
  找到一个最高山峰为起点,将它压入栈中,这样做会保证栈底一定会有一个比之后山峰高的山峰,每次遇到低于栈顶山峰的山峰直接压入栈中,遇到高于栈顶的山峰,此时栈顶的山峰就可以计算形成的山峰对,
  因为是可重复高度的,所以还要计算有几个相同高度自身形成的山峰对,是个组合数Cn2,这样一直到遍历一圈回到初始的最大山峰索引结束,结束后栈内若仍有元素,就一直弹出继续清算山峰对,直到栈为空。
public class Mountains {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        sc.nextLine();
        String[] s = sc.nextLine().split(" ");
        int[] arr = new int[n];
        for(int i = 0; i < n; i++) {
            arr[i] = Integer.parseInt(s[i]);
        }
        System.out.println(getAllPair(arr));
    }
    public static int getAllPair(int[] arr) {
        if(arr == null || arr.length < 2) return 0;
        int size = arr.length;
        int maxIndex = 0;
        for(int i =  1; i < size; i++) {
            if(arr[maxIndex] < arr[i]) maxIndex = i;
        }
        Stack<Record> stack = new Stack<>();
        stack.push(new Record(arr[maxIndex]));
        int res = 0, curIndex = nextIndex(maxIndex, size);
        while(maxIndex != curIndex) {
            while(arr[curIndex] > stack.peek().val) {
                int k = stack.pop().times;
                res += k * 2 + getInternal(k);
            }
            if(arr[curIndex] == stack.peek().val) {
                stack.peek().times++;
            } else {
                stack.push(new Record(arr[curIndex]));
            }
            curIndex = nextIndex(curIndex, size);
        }
        while(stack.size() > 2) {
            int k = stack.pop().times;
            res += getInternal(k) + k * 2;
        }
        if(stack.size() == 2) {
            int k = stack.pop().times;
            res += getInternal(k) + stack.peek().times == 1 ? k : k * 2;
        }
        res += getInternal(stack.pop().times);
        return res;
    }

    public static int getInternal(int n) {
        return n == 1 ? 0 : n * (n-1) / 2;
    }
    public static int nextIndex(int i, int size) {
        return i < size - 1 ? i + 1 : 0;
    }

}

class Record {
    int val, times;
    public Record(int val) {
        this.val = val;
        times = 1;
    }
}

 

posted @ 2020-07-07 15:28  Sexyomaru  阅读(186)  评论(0编辑  收藏  举报