不含负数,可能含有相同数值的山峰数组(环形)
山峰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; } }