[英]Is the runtime of my code O(n) or O(n * m)?
public List<List<Integer>> groupThePeople(int[] groupSizes) {
List<List<Integer>> groups = new ArrayList<List<Integer>>();
for(int i = 0; i < groupSizes.length; i++) {
boolean added = false;
int index = 0;
while(index < groups.size() && !added) {
List<Integer> temp = groups.get(index);
int size = groupSizes[temp.get(0)];
if(size == groupSizes[i] && temp.size() < groupSizes[i]) {
temp.add(i);
added = true;
}
else {
index++;
}
}
if(!added) {
ArrayList<Integer> temp = new ArrayList<Integer>();
temp.add(i);
groups.add(temp);
}
}
return groups;
}
我認為這段代碼以 O(n) 運行,因為我只遍歷數組一次,但由於內部循環,它也可以是 O(n * m) 嗎?
似乎在最好的情況下,復雜性可能是O(N)
- 當輸入數組groupSizes
包含 N 個不小於 N 的相同元素時,例如{5, 5, 5, 5, 5}, {10, 10,... 10} (10 elements), etc.
。
如果所有 N 個元素都不同,則復雜度為O(N^2)
(實際上是~N^2/2
)——這是最壞的情況,當groupSize
包含 N 個零或一個(N > 1)時也是可能的。
更新
可以重構此代碼以提供 O(N) 復雜度,借助映射將輸入數組中的數字存儲為鍵,並將輸入數組中的索引作為值收集到列表(2D 列表)中。
最好使用LinkedHashMap
因為它為插入和獲取操作提供 O(1) 並維護插入的順序。
public static List<List<Integer>> groupThePeople(int... groupSizes) {
// System.out.println("plain: " + Arrays.toString(groupSizes));
Map<Integer, List<List<Integer>>> map = new LinkedHashMap<>();
for (int i = 0; i < groupSizes.length; i++) {
// create a 2D list as a default value for the given map
List<List<Integer>> value = map.computeIfAbsent(groupSizes[i], k -> new ArrayList<>(Arrays.asList(new ArrayList<Integer>())));
// new list needed if no size left in the last available inner list
if (!value.get(value.size() - 1).isEmpty() && groupSizes[i] <= value.get(value.size() - 1).size()) {
value.add(new ArrayList<>());
}
// add index to the inner list
value.get(value.size() - 1).add(i);
}
return map.values() // convert Collection<List<List<Integer>>>
.stream()
.flatMap(x -> x.stream())
.collect(Collectors.toList());
}
測試
groupThePeople(0, 1, -1, -1, 3, 3, 3, 1, 2, 3, 0).forEach(System.out::println);
groupThePeople(10, 10, 10, 10, 10, 10, 10, 10, 10, 10).forEach(System.out::println); // all numbers the same
groupThePeople(10, 15, 15, 15, 10, 10, 10, 10, 10, 10).forEach(System.out::println); // without splitting nested lists
輸出:
plain: [0, 1, -1, -1, 3, 3, 3, 1, 2, 3, 0]
[0]
[10]
[1]
[7]
[2]
[3]
[4, 5, 6]
[9]
[8]
plain: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
plain: [10, 15, 15, 15, 10, 10, 10, 10, 10, 10]
[0, 4, 5, 6, 7, 8, 9]
[1, 2, 3]
類似的方法可以使用 Stream API 實現,盡管它看起來更冗長。
此外,這個實現是兩次傳遞,需要第二次傳遞將索引的內部列表拆分為大小小於鍵號的子列表。
public static List<List<Integer>> groupThePeopleStream(int... groupSizes) {
// System.out.println("stream: " + Arrays.toString(groupSizes));
return IntStream
.range(0, groupSizes.length)
.mapToObj(i -> Map.entry(groupSizes[i], i)) // entry: number -> index
.collect(Collectors.groupingBy(Map.Entry::getKey, LinkedHashMap::new, // convert to map: number -> list of indexes
Collectors.mapping(Map.Entry::getValue, Collectors.toList())))
.entrySet()
.stream()
//.peek(e -> System.out.println(e.getKey() + ":"))
.flatMap(e -> e.getKey() >= e.getValue().size() // need to split list of indexes?
? Stream.of(e.getValue()) // no, store a single index
: IntStream.iterate(0, x -> x < e.getValue().size(), x -> x + Math.max(1, e.getKey())) // yes, preparing indexes: 0, N, 2N..
.mapToObj(x -> e.getValue().subList(x, Math.min(x + Math.max(1, e.getKey()), e.getValue().size())))
)
//.peek(x -> System.out.println("\t" + x))
.collect(Collectors.toList());
}
相同測試數據的輸出(包括調試打印):
stream: [0, 1, -1, -1, 3, 3, 3, 1, 2, 3, 0]
0:
[0]
[10]
1:
[1]
[7]
-1:
[2]
[3]
3:
[4, 5, 6]
[9]
2:
[8]
stream: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
10:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
stream: [10, 15, 15, 15, 10, 10, 10, 10, 10, 10]
10:
[0, 4, 5, 6, 7, 8, 9]
15:
[1, 2, 3]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.