我试图为LinkedList编写一个Insertion排序,我有一个工作方法,但速度非常慢。一个多小时的时间来添加和排序50,000个元素。
public void insert(Custom c)
{
int i = 0;
for (i = 0; i < list.size(); i++)
{
if(list.get(i).compareTo(c) > 0 )
{
list.add(i,c);
return;
}
}
list.add(c);
}
我知道我可以使用Collections.Sort但是对于这个赋值我需要编写自己的LinkedList。我并没有要求提供完整的解决方案。
答案 0 :(得分:1)
首先,List
上的插入排序会变慢(O(N^2)
)......无论你怎么做。但您似乎已将其实施为O(N^3)
。
这是你的代码...将被称为N次,以添加每个列表元素。
public void insert(Entry e)
{
int i = 0;
for (i = 0; i < list.size(); i++) // HERE #1
{
if(list.get(i).compareTo(e) > 0 ) // HERE #2
{
list.add(i,e); // HERE #3
return;
}
}
list.add(e); // HERE #4
}
在“HERE#1”,我们迭代直到 M次,其中M是当前(部分)列表长度;即O(M)
。这是插入排序中固有的。但是,根据您实施size()
方法的方式,可以将迭代转换为O(M^2)
操作序列。 (LinkedList.size()
方法只返回size
变量的值。这里没问题。但如果size()
计算了元素...)
在“HERE#2”,我们有一个获取和比较。比较(compareTo(...)
)很便宜,但链接列表上的get(i)
操作涉及从头开始遍历列表。这是O(M)
操作。由于您通过get(i)
来电进行了O(M)
次insert
次通话,因此调用O(M^2)
和排序O(N^3)
。
在“HERE#3”,add(i,e)
重复上一次get(i)
电话的列表遍历。但这并不是那么糟糕,因为每add(i,e)
次呼叫只执行insert
次呼叫。因此总体复杂性不受影响。
在“此处#4”,add()
操作可以是O(1)
或O(M)
,具体取决于其实施方式。 (对于LinkedList.add()
,它是O(1)
,因为列表数据结构保留了对列表最后一个节点的引用。)无论哪种方式,总体复杂性都不会受到影响。
简而言之:
#2的代码肯定会使其成为O(N^3)
种。
#1的代码也可以O(N^3)
...但不能使用标准LinkedList
类。
那该怎么办?
一种方法是重新编码insert
操作,以便它直接使用next
和prev
字段等遍历列表。不应该调用任何“更高级别”列表操作:size,get(i),add(e)或add(i,e)。
但是,如果您通过扩展或包装LinkedList
来实现此功能,则不能选择此选项。那些领域是私人的。
如果要扩展或包装LinkedList
,则解决方案是使用listIterator()
方法为您提供ListIterator
,并将其用于有效遍历。 add
上的ListIterator
操作为O(1)
。
如果(假设)您正在寻找对(大)LinkedList
进行排序的最快方法,那么解决方案就是使用Collections.sort
。在幕后,该方法将列表内容复制到一个数组,对数组进行O(NlogN)
排序,并从排序后的数组中重建列表。
答案 1 :(得分:0)
根据this回复,由于性能更佳,您应使用ListIterator.add()代替List.add
。
答案 2 :(得分:0)
如何使用更快的排序算法?
以下是QuickSort。它比大型数据集的正常排序更快。 QuickSort的平均情况为O(nlogn),而插入的平均情况为O(n ^ 2)。不是很大的区别吗?
import java.util.*;
public class QuickSort{
public static void swap(int A[] , int x, int y){
int temp = A[x];
A[x] = A[y];
A[y] = temp;
}
public static int[] QSort(int A[],int L, int U){
Random randomGenerator = new Random();
if ( L >= U){
return A;
}
if (L < U) {
/*
Partion the array around the pivot, which is eventually placed
in the correct location "p"
*/
int randomInt = L + randomGenerator.nextInt(U-L);
swap(A,L,randomInt);
int T = A[L];
int p = L;
for(int i= L+1; i<= U; i++){
if (T > A[i]){
p = p+1;
swap(A,p,i);
}
}
/* Recursively call the QSort(int u, int l) function, this deals with
the upper pointer first then the lower.
*/
swap(A,L,p);
QSort(A,p+1,U);
QSort(A,L, p-1);
}
return A;
}
}
import java.util.*;
public class Main{
public static void main(String [] args){
int[] intArray = {1,3,2,4,56,0,4,2,4,7,80,120,99,9,10,67,101,123,12,-1,-8};
System.out.printf("Original Array was:\n%s\n\n",Arrays.toString(intArray));
System.out.printf("Size of Array is: %d\n\n",intArray.length);
QuickSort.QSort(intArray, 0, intArray.length - 1);
int num = Integer.parseInt(args[0]);
System.out.println("The sorted array is:");
System.out.println(Arrays.toString(intArray));
}
}
上面的示例将对Int数组进行排序,但您可以轻松编辑它以对任何对象进行排序(例如,在您的情况下为Entry)。我会让你自己解决这个问题。
祝你好运
答案 3 :(得分:0)
list.add(e)
和list.get(e)
每次调用时都会占用o(n)。旅行时,你应该避免使用它们。
相反,如果您必须编写自己的链接列表,则应该跟踪您正在旅行的元素。通过替换操作i ++并通过elem = elem.next
或elem = elem.getnext()
得到(i),(可能还有别的,取决于你如何实现你的链表)。然后通过执行以下操作添加元素:
elem.next.parent = e;
e.next = elem.next;
elem.next = e;
e.parent = elem;
这里我的示例适用于双向链表,elem表示您当前正在比较要添加的对象的链表中的元素。