英文:
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),其中 n 是输入数组 groupSizes 的长度。尽管有一个内部的 while 循环,但需要注意的是,内部循环的总迭代次数最多只会是输入数组的长度 n,而不是 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;
}
I am thinking that this code is running at O(n) because I only iterate through the array once, but can it also be O(n * m) because of the inner loop?
答案1
得分: 1
以下是您提供的内容的翻译部分:
似乎在最好的情况下,复杂度可能为 O(N)
- 当输入数组 groupSizes
包含 N 个不少于 N 的相同元素,如 {5, 5, 5, 5, 5}, {10, 10,... 10} (10 个元素),等等
。
如果所有 N 个元素都不同,复杂度为 O(N^2)
(实际上是 ~N^2/2
) - 这是最坏的情况,当 groupSize
包含 N 个零或一个的时候也可能发生(N > 1)。
更新:
通过使用映射将输入数组中的数字存储为键,并将输入数组中的索引收集到列表(二维列表)中,可以重构此代码以提供 O(N) 的复杂度。
最好使用 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++) {
// 创建一个二维列表作为给定映射的默认值
List<List<Integer>> value = map.computeIfAbsent(groupSizes[i], k -> new ArrayList<>(Arrays.asList(new ArrayList<Integer>())));
// 如果上一个可用的内部列表中没有剩余大小,则需要新的列表
if (!value.get(value.size() - 1).isEmpty() && groupSizes[i] <= value.get(value.size() - 1).size()) {
value.add(new ArrayList<>());
}
// 将索引添加到内部列表
value.get(value.size() - 1).add(i);
}
return map.values() // 转换为 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); // 所有数字相同
groupThePeople(10, 15, 15, 15, 10, 10, 10, 10, 10, 10).forEach(System.out::println); // 不拆分嵌套列表
输出:
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]
英文:
It seems that in best case the complexity may be O(N)
- when the input array groupSizes
contains N identical elements not less than N like {5, 5, 5, 5, 5}, {10, 10,... 10} (10 elements), etc.
.
If all N elements are different, the complexity is O(N^2)
(actually, ~N^2/2
) - this is the worst case which is also possible when groupSize
contains N zeros or ones (N > 1).
Update<br/>
This code may be refactored to provide O(N) complexity with the help of using a map to store the numbers in the input array as keys and indexes in the input array collected into lists (2D lists) as values.
It's better to use LinkedHashMap
because it provides O(1) for insertion and get operations and maintains the order of insertion.
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());
}
Test
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
Output:
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]
Similar approach may be implemented using Stream API, though it seems more verbose.
Also, this implementation is two-pass, the second pass is needed to split the inner lists of indexes into sublists with the size less than the key number.
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());
}
Output for the same test data (including debug prints):
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]
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论