繁体   English   中英

我的代码的运行时间是 O(n) 还是 O(n * m)?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM