问题描述:一个不含有负数的数组可以代表一圈环形山,每个位置的值代表山的高度。
例:{1,2,4,5,3}、{3,1,2,4,5}、{4,5,3,1,2}都代表同样结构的环形山
1->2->4->5->3->1方向叫做next方向(逆时针),1->3->5->4->2->1方向叫做last方向(顺时针)。
山峰A和山峰B能够相互看见的条件为:
1.若他们是同一座山,认为不能相互看见
2.若他们是不同的山,并且在环中相邻,认为可以相互看见
3.若A和B是不同的山,并且在环中不相邻,假设两座山高度的最小值为min。如果A通过next方向到B的途中没有比min大的山峰,或者A通过last方向到B的途中没有高度比min大的山峰,则A和B可以相互看见
分析1.:
环形中只有一座山峰时,可见山峰对的数量为0,即返回0
环形中有两座山峰时,可见山峰对的数量为1
环形结构中有n座山峰时:
用高度小的山峰找高度大的山峰:
如上图,从2开始找,找比它大的,沿着next方向,4比它大,沿着last方向,3比它大,所以找到(2,4),(2,3)
环中必存在唯一的最大值和唯一的此大值。
除了最大值和此大值还剩n-2个结点,这些结点根据小找大,每一个都能找到两对,并且还有次大值和最大值这一对,所以一共:2*(n-2)+1对
时间复杂度O(1)
分析二:
一开始,遍历一次环形山结构,找到最大值的位置,最大值可能不止一个,但取任何一个都可以,将其放入一个stack栈中,即(5,1),表示高度为5,收集了一个。
从最大值开始沿着next方向再遍历一遍环形山。
说明当前弹出的3在next方向上遇到的第一个大于它的数就是4,在last方向上遇到的第一个大于它的数就是5.则从当前的3出发,通过小找大,可以找到两个可见山峰对(3,4),(3,5)
继续上面的步骤。
则:
在遍历期间,记录(N,k)从stack弹出了,若k为1,则产生两对可见山峰对,若k>1,产生2*k+C(2,k)对。
时间复杂度为O(n)
代码实现:
import java.io.BufferedInputStream;
import java.util.Scanner;
import java.util.Stack;
public class TestVisible {
public int data;
public int times;
//记录数字和收集的次数
public TestVisible(int value) {
this.data = data;
this.times = 1;
}
public static int nextIndex(int i, int size) { //返回下一个位置
if (i < size - 1) {
return i + 1;
} else {
return 0;
}
}
public static int getNumbers(int n) {
if (n == 1) {
return 0;
} else {
return n * (n - 1) / 2; //C(n,2)
}
}
public static int VisibleNumber(int[] array) {
if (array == null || array.length < 2) {
return 0; //可见对数为0
}
int mIndex = 0; //最大值的位置
for (int i = 0; i < array.length; i++) {
if (array[mIndex] < array[i]) {
mIndex = i;
} else {
mIndex= mIndex;
}
}
Stack<TestVisible> stack = new Stack<TestVisible>();
stack.push(new TestVisible(array[mIndex])); //(最大值,1)入栈
int index = nextIndex(mIndex, array.length); //沿next方向遍历
int resultNumber = 0; //统计山峰对
while (index != mIndex) { //若相等,则转完一圈回来了
while (!stack.isEmpty()&&stack.peek().data < array[index]) { //和栈顶比较,若该数大,则弹出栈顶元素
int k = stack.pop().times;
resultNumber += getNumbers(k) + 2 * k;
}
if (!stack.isEmpty()&&stack.peek().data == array[index]) {
stack.peek().times++; //数字一样就合并
} else {
stack.push(new TestVisible(array[index]));
}
index = nextIndex(index, array.length);
}
while (stack.size() > 2) { //弹出的记录不是栈中最后一个记录,也不是倒数第二个
int times = stack.pop().times;
resultNumber += getNumbers(times) + 2 * times;
}
while (stack.size() == 2) { //弹出的记录是栈中倒数第二个
int times = stack.pop().times;
if (stack.peek().times == 1) {
resultNumber += getNumbers(times) + times;
} else {
resultNumber += getNumbers(times) + 2 * times;
}
}
resultNumber += getNumbers(stack.pop().times); //弹出最后一个记录
return resultNumber;
}
public static void main(String[] args) {
System.out.println("please input the array: ");
Scanner scan = new Scanner(new BufferedInputStream(System.in));
while (scan.hasNext()) {
int n = scan.nextInt();
int[] array = new int[n];
for (int i = 0; i < n; i++) {
array[i] = scan.nextInt();
}
System.out.println(VisibleNumber(array));
}
}
共勉,有问题望及时指出