Reputation: 11
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?
Upvotes: 0
Views: 56
Reputation: 19555
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
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]
Upvotes: 1