题目:摩天大楼

                      

题目

你有一个包含N个整数的数组A,这些整数代表要建造的N座摩天大楼的最大高度。你的任务是指定这些摩天大楼的实际高度,需满足以下条件:

  • 第K座摩天大楼的高度应该是正数,并且不超过A[K];
  • 不应有两座摩天大楼的高度相同;
  • 所有摩天大楼的高度总和应该尽可能的大。

编写一个函数,给定一个整数数组A,返回一个包含N个整数的数组B,其中B[K]是第K座摩天大楼满足上述条件的指定高度。

如果有多个可能的答案,函数可以返回其中任何一个。你可以假设总是可以在满足所有要求的情况下建造所有摩天大楼。

示例:

  • 给定 A = [1, 2, 3],你的函数应该返回 [1, 2, 3],因为所有摩天大楼都可以建造到它们的最大高度。
  • 给定 A = [9, 4, 3, 7, 7],你的函数可以返回 [9, 4, 3, 7, 6]。注意 [9, 4, 3, 6, 7] 也是一个有效答案。不可能让最后两座摩天大楼的高度相同。其中一座的高度应该是7,另一座是6。
  • 给定 A = [2, 5, 4, 5, 5],你的函数应该返回 [1, 2, 3, 4, 5]。

编写一个高效的算法,基于以下假设:

  • N是一个范围在[1…50,000]内的整数;
  • 数组A的每个元素都是一个范围在[1…1,000,000,000]内的整数;
  • 给定输入总是有解。

大体思路

这道题目的是求出尽可能大的摩天大楼总和的高度,由于这里有n座摩天大楼,那么我们可以把这个问题理解为每座摩天大楼的高度尽可能的高,从而我们可以将这道题转化为求每座摩天大楼的最大高度。


思路1(超时)

1.使用hashmap,key为要建立的高楼高度,value为高楼位置
遍历原数组
2.如果当前高楼没有建立过相同高度,则在该位置建立该高度的高楼,map.put(height, index)
3.如果当前高楼建立过,则while循环向下寻找,最可能建立的最大高楼的高度,map.put(tmpHeight, index)
以上保证尽可能高的建立高楼
4.最后创建结果数组,遍历map,一一赋值要建立的高楼位置及其高度

方法1 Code(超时)
public class Solution1 {
    public int[] solutionTimeOut(int[] A) {
        Map<Integer, Integer> resultMap = new HashMap();
        for (int i = 0; i < A.length; i++) {
            int tmp = A[i];
            while (resultMap.containsKey(tmp)) {
                tmp--;
            }
            resultMap.put(tmp, i);
        }
        System.out.println(resultMap); //{3=2, 4=1, 6=4, 7=3, 9=0}

        int[] result = new int[A.length];
        Iterator iterator = resultMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Integer, Integer> entry = (Map.Entry<Integer, Integer>) iterator.next();
            result[entry.getValue()] = entry.getKey();
        }
        return result;
    }
}
resultMap
第一次{9=0}
第二次{9=0,4=1}
第三次{9=0,4=1,3=2}
第四次{9=0,4=1,3=2, 7=3}
第五次{9=0,4=1,3=2, 7=3, 6=4}
复杂度

时间复杂度:O(MN), 10^9 * 5 * 10^4
空间复杂度:O(n), n < 5 * 10^4

思路2

给定A = [2, 5, 4, 5, 5],想要拿到创造摩天大楼总和的最大值,尽可优先对具有最大高度的摩天大楼创建,假如有三栋楼,可造高楼的最大值分别为5,5,5, 那么为了总和最大,首先对具有最大高度的楼进行建造。按照这种思想,我们可以先对给定的数组进行排序。

我们可以先降序找到每个数字出现的次数。


创建sortedMap,遍历数组初始化map,key为出现的高度,value为该高度出现的重复次数
更新sortedMap,key为出现的高度,value为该高度对应的最大能建立的高度 (重点)
遍历原数组,遇到某高度大楼时,从map中取value为其能建立的最大高度,同时高度-1
示例:
{2,5,4,5,5}
{5=3, 4=1, 2=1} - sortedMap 初始化  最大高度-出现次数
{5=5, 4=2, 2=1} - sortedMap 更新     最大高度-每个最大高度第一个位置的最大可建造高度

sortMap更新过程:

lastMaxHeightcurrentHeight
初始

Integer.MAX_VALUE

12(因为5出现了3次, 所以依次递减最大建造高度为5时可分配高度5-3)5  min(上一次的最大高度,这座摩天大楼最大高度)
212  min(2,4)
31  min(1,2)


复杂度


时间复杂度:O(nlogM), n = 5 * 10^4, m = 10^9
空间复杂度:O(n), n= 5 * 10^4
 

方法2 Code
public class Solution {
    public int[] solution(int[] A) {
        // 创建一个新的 TreeMap,用于按高度降序存储每个高度出现的次数
        TreeMap<Integer, Integer> sortedMap = new TreeMap<>((o1, o2) -> o2 - o1);

        // 遍历数组 A,更新 TreeMap 中每个高度出现的次数
        for (int i = 0; i < A.length; i++) {
            sortedMap.put(A[i], sortedMap.get(A[i]) == null ? 1 : sortedMap.get(A[i]) + 1);
        }

        // 更新 TreeMap 中每个高度对应的最大高度
        int[] result = new int[A.length];
        Iterator iterator = sortedMap.entrySet().iterator();  //[5=3, 4=1, 2=1]
        int lastMaxHeight = Integer.MAX_VALUE;

        while (iterator.hasNext()) {
            Map.Entry<Integer, Integer> entry = (Map.Entry<Integer, Integer>) iterator.next();
            System.out.println("getKey"+ entry.getKey());
            System.out.println("lastMaxHeight: " + lastMaxHeight);

            System.out.println("getvalue"+ entry.getValue());
            int currentHeight = Math.min(entry.getKey(), lastMaxHeight);
            lastMaxHeight = currentHeight - entry.getValue();
            entry.setValue(currentHeight);
            System.out.println("lastMaxHeight: " + lastMaxHeight + ", currentHeight: " + currentHeight);
        }
        //enrty {5=5, 4=2, 2=1}

        // 设置结果数组,根据 TreeMap 中的最大高度分配高度给每个摩天大楼
        for (int i = 0; i < A.length; i++) {
            result[i] = sortedMap.get(A[i]);
            sortedMap.put(A[i], sortedMap.get(A[i]) - 1);
        }

        return result;
    }
}

遍历 sortMap : 取完当前的摩天大楼的最大高度之后,相同的最大高度要--

sortMap{5=5, 4=2, 2=1}  A{2,5,4,5,5}

{5=5, 4=2, 2=0}
{5=4, 4=2, 2=0}
{5=4, 4=1, 2=0}
{5=3, 4=1, 2=0}
{5=2, 4=1, 2=0}

思路3 
  1. 降序排序: 首先,将数组 A 降序排序。这样可以从最高的大楼开始,为每座大楼分配尽可能高的高度。

  2. 分配递减的高度: 从可能的最大高度(排序后数组的第一个元素)开始分配高度,然后对后续的每座大楼递减高度。我们需要确保每个分配的高度小于或等于排序后数组中的对应高度。

  3. 处理重复值: 如果一个高度已经被分配,我们需要减少它以确保其唯一性。我们可以跟踪上一个分配的高度,确保下一个高度总是更小。

  4. 优化总高度的最大化: 为了最大化总高度,总是尽量为每座大楼分配尽可能高的高度。

复杂度

时间复杂度:O(N log N) 

空间复杂度 O(N)

方法3 code
public class Solution7 {
    static class Skyscraper {
         int height;
         int index;

        Skyscraper(int height, int index) {
            this.height = height;
            this.index = index;
        }
    }

    public int[] solution(int[] A) {
        int n = A.length;
        Skyscraper[] skyscrapers = new Skyscraper[n];

        // 高度和索引
        for (int i = 0; i < n; i++) {
            skyscrapers[i] = new Skyscraper(A[i], i);
        }

        // 降序
        Arrays.sort(skyscrapers, (s1, s2) -> Integer.compare(s2.height, s1.height));
        

        // 初始化
        int[] B = new int[n];
        int currentMaxHeight = skyscrapers[0].height;

        // 分配高度
        for (int i = 0; i < n; i++) {
            B[skyscrapers[i].index] = Math.min(currentMaxHeight, skyscrapers[i].height);
            currentMaxHeight = Math.max(1, B[skyscrapers[i].index] - 1);
        }

        return B;
    }
}
复杂度

时间复杂度:O(N log N) 

空间复杂度 O(N)

补充知识点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值