题目
你有一个包含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更新过程:
lastMaxHeight | currentHeight | |
初始 | Integer.MAX_VALUE | |
1 | 2(因为5出现了3次, 所以依次递减最大建造高度为5时可分配高度5-3) | 5 min(上一次的最大高度,这座摩天大楼最大高度) |
2 | 1 | 2 min(2,4) |
3 | 1 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
-
降序排序: 首先,将数组 A 降序排序。这样可以从最高的大楼开始,为每座大楼分配尽可能高的高度。
-
分配递减的高度: 从可能的最大高度(排序后数组的第一个元素)开始分配高度,然后对后续的每座大楼递减高度。我们需要确保每个分配的高度小于或等于排序后数组中的对应高度。
-
处理重复值: 如果一个高度已经被分配,我们需要减少它以确保其唯一性。我们可以跟踪上一个分配的高度,确保下一个高度总是更小。
-
优化总高度的最大化: 为了最大化总高度,总是尽量为每座大楼分配尽可能高的高度。
复杂度
时间复杂度: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)