具体问题是:
n 个 烽火台围成一个圈,任意两个烽火台只要中间的烽火台比他们两个都低就能看见彼此,当然相邻的肯定能看见对面,求能看见彼此的对数。
我们第一步就是找出这一圈数值中的最大值。为什么?因为这样我们就能确保后面的数向左至少和最大值能配成一对烽火台,我们只要观察右边的即可。
public static long communications(int [] arr)
{
if(arr==null||arr.length<2)
{
return 0;
}
int size=arr.length;
int maxIndex=0;
for(int i=0;i<size;i++)//遍历所有元素找到最大的那个值的序号
{
maxIndex=arr[maxIndex]<arr[i]?i:maxIndex;
//当前最大值比后面的小就更新maxIndex的值
}
int value=arr[maxIndex];//记录最大的值
int index=nextIndex(size,maxIndex);//获得最大值的下一个数
long res=0L;//能相互看见烽火台的对数
Stack<Pair>stack=new Stack<Pair>();
//前面循环可知value是最大值,将最大值的次数和值大小压入栈中
stack.push(new Pair(value));
while(index!=maxIndex)
{
value=arr[index];
while(!stack.isEmpty()&&stack.peek().value<value)
//栈不空且栈顶元素小于下一个数,那么就弹出栈顶
{
int times=stack.pop().times;
res+=getInternalSum(times)+2*times;//C(2,times)
}
if(!stack.isEmpty()&&stack.peek().value==value)
{
stack.peek().times++;
}else
{
stack.push(new Pair(value));
}
index=nextIndex(size,index);//下一个数
}
//栈中剩余的数
while(!stack.isEmpty())
//1、如果是倒数第三及以上一定是C(2,times)+2*k
//2、如果是倒数第二,倒数第一times为1,C(2,times)+k
//3、如果是倒数第二,倒数第一times为2,C(2,times)+2*k
{
int times=stack.pop().times;
res+=getInternalSum(times);//C(2,times)内部必产生山峰对
if(!stack.isEmpty())
{
res+=times;//这初步满足了2、的情况,下面判断是不是3、的情况
if(stack.size()>1)//不是倒数第一
{
res+=times;//满足了1、的情况
}else { //size==1,看最大数的times是1还是为k>1
//如果times>1就是 res+=times满足3、的情况
//吐过times不满足,那么就是res=res+0;满足了2、的情况
res+=stack.peek().times>1?times:0;
}
}
}
return res;
}
public static int nextIndex(int size,int i)
//因为是环,所以设置了size确定是否到达底部,未达到就i+1,达到了就置为0,说明到达了开头
{
return i<(size-1)?(i+1):0;
}
public static long getInternalSum(int n)
{
return n==1L?0L:(long)n*(long)(n-1)/2L;
}
public static class Pair{
//建一个类,stack中存储的就是Pair类型,值+次数
public int value;
public int times;
public Pair(int value)
{
this.value=value;
this.times=1;
}
}
public static void main(String[] args) {
int []arr= {1,2,4,5,3};
System.out.println(communications(arr));
}