使用Java Stream构建二叉树。是否有可能在Java Stream排序时减少?

时间:2018-03-06 23:44:22

标签: sorting java-8 java-stream reduce

我想使用Java Stream从输入字符串构建Huffman tree

这就是我现在的做法。

使用所有需要的构造函数的类MyNode:

public static class MyNode {
    Character value;
    MyNode left;
    MyNode right;
    long freq;
    ...
}

读取一行并获取MyNodes列表:

Scanner scan = new Scanner(System.in);
String input = scan.next();
List<MyNode> listOfNodes = input.chars().boxed()
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) 
            .entrySet()
            .stream().sorted(Comparator.comparingLong(Map.Entry::getValue))
            .map(x -> new MyNode((char)x.getKey().intValue(), x.getValue()))
            .collect(Collectors.toList());

这个while循环我想用Stream中的内容替换:

while (listOfNodes.size() > 1) {
        MyNode first = listOfNodes.get(0);
        MyNode second = listOfNodes.get(1);
        listOfNodes.remove(first);
        listOfNodes.remove(second);
        listOfNodes.add(new MyNode(first.freq + second.freq, first, second));
        listOfNodes.sort(Comparator.comparingLong(MyNode::getFreq));
    }

在while循环中,我构建了这样的while loop pic

第一个想法是使用Stream reduce,但是我需要在每次reduce之后对结果列表进行排序。

1 个答案:

答案 0 :(得分:2)

这不是一项受益于使用Stream API的任务。不过,有一些方法可以改进它。

仅为了插入单个元素而对整个列表进行排序,会产生不必要的开销。由于列表是按照开头排序的,因此您可以使用二进制搜索来有效地找到正确的插入位置,以便列表保持排序:

while(listOfNodes.size() > 1) {
    MyNode first = listOfNodes.remove(0), second = listOfNodes.remove(0);
    MyNode newNode = new MyNode(first.freq + second.freq, first, second);
    int pos = Collections.binarySearch(listOfNodes, newNode,
                                       Comparator.comparingLong(MyNode::getFreq));
    listOfNodes.add(pos<0? -pos-1: pos, newNode);
}

请注意,您可以通过撤消订单来提高此代码的效率,以便从列表末尾删除(实际上这将是ArrayList)。

但更好的选择是使用一个数据结构,该数据结构按开头排序,例如

PriorityQueue<MyNode> queueOfNodes = input.chars().boxed()
    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) 
    .entrySet().stream()
    .map(x -> new MyNode((char)x.getKey().intValue(), x.getValue()))
    .collect(Collectors.toCollection(
        () -> new PriorityQueue<>(Comparator.comparingLong(MyNode::getFreq))));

MyNode result = queueOfNodes.remove();
while(!queueOfNodes.isEmpty()) {
    MyNode second = queueOfNodes.remove();
    queueOfNodes.add(new MyNode(result.freq + second.freq, result, second));
    result = queueOfNodes.remove();
}