可见山峰对数量练习

问题描述:一个不含有负数的数组可以代表一圈环形山,每个位置的值代表山的高度。
例:{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));
        }
    }


共勉,有问题望及时指出

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值