给定一个预先排序的数组列表,即List<List<Integer>>
,我使用以下技术对它们进行排序。有人请向我解释时间的复杂性吗?这是算法
List<Integer> merge(List<List<Integer>> lists){
Queue<MyPriorityQueue> pqs = new LinkedList<>();
for(int i=1; i<lists.size();i+=2){
pqs.add(merge(lists.get(i-1),lists.get(i)));
}
if(lists.size()%2==1)pqs.add(merge(lists.get(lists.size()-1),null));
while(pqs.size()>1) pqs.add( merge(pqs.poll(),pqs.poll()));
MyPriorityQueue myPq = pqs.poll();
List<Integer> results = new ArrayList<>();
while(!myPq.isEmpty()){
result.add(myPq.poll());
}
return result;
}
有两种merge
方法。
MyPriorityQueue merge(List<Integer> a, List<Integer> b){ return new MyPriorityQueue(a,b);}
MyPriorityQueue merge(MyPriorityQueue a, MyPriorityQueue b){ return new MyPriorityQueue(a,b);}
请注意,merge
函数实际上将两个迭代器传递给MyPriorityQueue构造函数。因此,当调用MyPriorityQueue#poll()
时,将比较两个迭代器并返回更大的值。因此,我们没有使用额外的内存。
现在让我们与普通的mergeSort进行比较。通常mergeSort使用额外的内存:每次调用merge(x,y)只会创建第三个列表z并将x和y中的所有元素放入z中。该方法将导致时间O(n lg n)。但在我目前的方法中,我们并没有使用额外的内存。因此每次调用#poll()时,我都必须走树才能找到最小的元素。这对我来说意味着行while(!myPq.isEmpty()){result.add(myPq.poll());}
的O(n ^ 2)。但是有人告诉我时间复杂度实际上仍然是O(n lg n)。我看不出怎么样。有人请向我解释时间复杂度吗?
我们没有进行n次比较n次,是吗?根据我的可视化,如果树上有n个列表并且我调用myPq.poll())
,那么我们正在进行n次比较。
所以基本上让我们说有n个列表,每个列表中平均有k个元素。我认为算法的时间复杂度
log n + (n+k)*n
。创建pqs树并获取根目录需要log n;然后将(n + k)* n作为while(!myPq.isEmpty()){result.add(myPq.poll());}
更新:这是Java中的实际实现
import java.util.*;
public class MergePresortedLists {
List<Integer> merge(List<List<Integer>> lists) {
Queue<PQ> pqs = new LinkedList<>();
for (int i = 1; i < lists.size(); i += 2) {
pqs.add(merge(lists.get(i - 1), lists.get(i)));
}
if (lists.size() % 2 != 0)
pqs.add(merge(lists.get(lists.size() - 1), null));
while (pqs.size() > 1) {
pqs.add(merge(pqs.poll(), pqs.poll()));
}
PQ pq = pqs.poll();
List<Integer> result = new ArrayList<>();
while (!pq.isEmpty()) {
result.add(pq.poll());
}
return result;
}
private PQ merge(List<Integer> a, List<Integer> b) {
return new PQ(a, b);
}
private PQ merge(PQ a, PQ b) {
return new PQ(a, b);
}
class PQ {
List<Integer> a, b;
int sizeA = -1, sizeB = -1;
PQ x, y;
public PQ(List<Integer> l1, List<Integer> l2) {
a = l1;
b = l2;
if (null != a)
sizeA = a.size() - 1;
if (null != b)
sizeB = b.size() - 1;
}
public PQ(PQ q1, PQ q2) {
x = q1;
y = q2;
}
public int size() {
int listsSize = sizeA + sizeB + 2;
int qSize = null == x ? 0 : y.size();
qSize += null == y ? 0 : x.size();
return listsSize > qSize ? listsSize : qSize;
}
public boolean isEmpty() {
return size() == 0;
}
public Integer poll() {
if (sizeA > -1 || sizeB > -1) {
return listPoll();
}
Integer xPeek = (x != null && !x.isEmpty()) ? x.peek() : null;
Integer yPeek = (y != null && !y.isEmpty()) ? y.peek() : null;
if (null != xPeek || null != yPeek) {
return qPoll(xPeek, yPeek);
}
return null;
}
private Integer listPoll() {
if (sizeA > -1 && sizeB > -1) {
return a.get(sizeA) > b.get(sizeB) ? a.get(sizeA--) : b.get(sizeB--);
} else if (sizeA > -1)
return a.get(sizeA--);
else
return b.get(sizeB--);
}
private Integer qPoll(Integer xPeek, Integer yPeek) {
if (null != xPeek && null != yPeek) {
return xPeek > yPeek ? x.poll() : y.poll();
} else if (null != xPeek)
return x.poll();
else
return y.poll();
}
public Integer peek() {
if (sizeA > -1 || sizeB > -1) {
return listPeek();
}
Integer xPeek = null != x ? x.peek() : null;
Integer yPeek = null != y ? y.peek() : null;
if (null != xPeek || null != yPeek) {
return qPeek(xPeek, yPeek);
}
return null;
}
private Integer listPeek() {
if (sizeA > -1 && sizeB > -1) {
return a.get(sizeA) > b.get(sizeB) ? a.get(sizeA) : b.get(sizeB);
} else if (sizeA > -1)
return a.get(sizeA);
else
return b.get(sizeB);
}
private Integer qPeek(Integer xPeek, Integer yPeek) {
if (null != xPeek && null != yPeek) {
return xPeek > yPeek ? xPeek : yPeek;
} else if (null != xPeek) return xPeek;
else return yPeek;
}
}
}