我正在解决一个碎片问题。
- 假设我有10个列表。
- 每个列表都有一系列独立排序的项目。
- 我想得到第N个项目,就好像所有列表都被排序在一个大列表中一样。
我是否需要对列表进行整体排序,以获取特定索引处的项?
我解决了一个类似但不等价的问题,其中有:
- 10份名单
- 每个列表表示上一个列表之后的项目范围。
下面是遍历列表的所有索引的代码:
/* code to iterate through all items in order
* threads refers to one of the lists */
int sizes[] = new int[threads.size()];
for (int i = 0 ; i < threads.size(); i++) {
sizes[i] = threads.get(i).data2.size();
}
int n = 0;
int thread = 0;
int size = threads.size();
int offset = 0;
long iterationStart = System.nanoTime();
while (thread < size) {
// System.out.println(String.format("%d %d", thread, offset + threads.get(thread).data.get(n)));
int current = offset + threads.get(thread).data.get(n);
n = n + 1;
if (n == sizes[thread]) {
offset += sizes[thread];
thread++;
n = 0;
}
}
long iterationEnd = System.nanoTime();
long iterationTime = iterationEnd - iterationStart;
下面是通过索引查找项目的代码。
int lookupKey = 329131;
int current = lookupKey;
int currentThread = 0;
int total = 0;
while (current >= 0 && currentThread <= size - 1) {
int next = current - sizes[currentThread];
if (next >= 0) {
total += sizes[currentThread];
current -= sizes[currentThread];
currentThread++;
} else {
break;
}
}
long lookupEnd = System.nanoTime();
long lookupTime = lookupEnd - lookupStart;
System.out.println(String.format("%d %d",
currentThread,
total + threads.get(currentThread).data.get(current)));
我希望排序集合中有一些属性,可以用来检索整个排序列表中的第N项。
我手上的是多个部分订单。
我有一些其他的代码可以在多个排序列表之间进行N路合并。在一个循环中运行这个代码到lookupIndex是最快的选择吗?
int size1 = threads.size();
int[] positions = new int[size1];
Arrays.fill(positions, 0);
PriorityQueue<Tuple> pq = new PriorityQueue<>(new Comparator<Tuple>() {
@Override
public int compare(Tuple o1, Tuple o2) {
return o1.value.compareTo(o2.value);
}
});
long startOrderedIteration = System.nanoTime();
for (ShardedTotalRandomOrder thread : threads) {
for (int i = 0; i < 10; i++) {
// System.out.println(thread.data2.get(i));
pq.add(thread.data2.get(i));
}
}
List<Integer> overall = new ArrayList<>();
while (!pq.isEmpty()) {
Tuple poll = pq.poll();
ArrayList<Tuple> data2 = threads.get(poll.thread).data2;
if (positions[poll.thread] < data2.size()) {
Tuple nextValue = data2.get(positions[poll.thread]++);
pq.offer(nextValue);
}
overall.add(poll.value);
// System.out.println(String.format("%d %d", poll.thread, poll.value));
}
System.out.println(overall);
long endOrderedIteration = System.nanoTime();
long orderedIterationTime = endOrderedIteration - startOrderedIteration;
3条答案
按热度按时间vktxenjb1#
你不需要重新排序它们。因为每个列表都已经排序过了,你可以按如下方式合并它们。它使用一个方法根据它们的相对值合并两个列表。然后它返回那个列表并将它反馈给方法,以将它与下一个列表合并。
印刷品
l2osamch2#
这是一个相对高效(与列表数量呈线性关系)的算法,它利用了流的一些功能,但避免了完全的列表合并。
EDIT:为了解决数组长度检查、数组破坏和可读性等缺点,我改进了这个例子。为了更好地进行比较,我使用了与另一个答案相同的整数测试数据。
这个由(大概)不可变数组支持的虚拟队列将不会发生变化或其他情况
(我怀疑用标准集合有更简单的方法来做到这一点)
goqiplq23#
假设您有
k
排序列表,并且您需要从聚合列表中获得n
(但合并列表本身不需要),那么这个问题可以在**O(n * log k)时间内解决,并且使用O(k)**额外空间。注:
List.sort()
使用内置的Timsort算法实现。Timsort非常擅长发现排序后的运行,因此对由排序块组成的列表进行排序将具有线性时间复杂度。*为了在O(n * log k)时间内解决该问题,我们可以维护一个
PriorityQueue
,它将总是具有k
或更小的大小( 因此入队/出队操作将具有O(log k)**的成本)。在开始时,应该通过添加来自每个List的第一个元素来初始化Queue。然后我们需要执行
n
迭代在每个迭代步骤中,队列的Head元素应该被移除,并且源自同一列表的下一个元素应该被添加到队列(即,如果我们说来自第三列表的第7个元素看起来是队列头,那么在移除它之后,我们需要将来自第三列表的第八元素入队)。为了能够跟踪每个元素来自哪个List,以及它在List中的索引是什么,我们可以定义一个自定义类型:
下面是查找
n
元素的算法的实现方法,如前所述,时间复杂度为O(n * log k),因为我们需要n
迭代步骤,每一步的开销为O(log k),额外的内存只需要用来维护k
元素的队列。***注意:*根据您希望如何为第
n
个元素建立索引,getNElement()
方法中的count
变量应相应地进行初始化,例如,如果您希望使用基于1
的索引,则使用1
;如果您希望n
基于0
,则使用0
。