[英]Insertion Sort LinkedList Java
我正在嘗試為LinkedList寫一個插入排序,我有一個有效的方法,但是它的運行速度非常慢。 一個多小時即可添加和排序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。 我不要求僅提供一些提示的完整解決方案。
首先,對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)
操作。 並且由於您使每個insert
調用的get(i)
調用O(M)
次,因此使調用O(M^2)
和排序O(N^3)
。
在“ HERE#3”處, add(i,e)
重復前一個get(i)
調用的列表遍歷。 但這還不錯,因為每個insert
調用只執行一次add(i,e)
調用。 因此,總體復雜度不受影響。
在“ HERE#4”處,取決於實現方式, add()
操作可以為O(1)
或O(M)
。 (對於LinkedList.add()
它為O(1)
因為列表數據結構保留對列表最后一個節點的引用。)兩種方式都不會影響總體復雜性。
簡而言之:
#2處的代碼肯定使它成為O(N^3)
排序。
#1處的代碼也可以將其設置為O(N^3)
...,但不能使用標准LinkedList
類。
那么該怎么辦?
一種方法是重新編碼insert
操作,以便它直接使用next
和prev
字段等遍歷列表。 不應調用任何“高級”列表操作:大小,get(i),add(e)或add(i,e)。
但是,如果要通過擴展或包裝LinkedList
來實現此目的,則不是一種選擇。 這些字段是私有的。
如果要擴展或包裝LinkedList
,則解決方案是使用listIterator()
方法為您提供ListIterator
,並將其用於有效的遍歷。 ListIterator
的add
操作為O(1)
。
如果(假設)您正在尋找對(大型) LinkedList
進行排序的最快方法,那么解決方案是使用Collections.sort
。 在O(NlogN)
,該方法將列表內容復制到數組,對數組進行O(NlogN)
排序,然后從排序后的數組中重建列表。
根據此響應,由於性能更好,因此應使用ListIterator.add()而不是List.add
。
使用更快的排序算法呢?
這就是所謂的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)。 病會讓你自己弄清楚。
祝好運
list.add(e)
和list.get(e)
)每次調用都將占用o(n)。 您在旅行列表時應避免使用它們。
相反,如果您必須編寫自己的鏈接列表,則應跟蹤所旅行的元素。 通過將操作i ++和get(i)替換為elem = elem.next
或elem = elem.getnext()
,(可能還取決於您實現鏈接列表的方式)。 然后通過執行以下操作添加一個元素:
elem.next.parent = e;
e.next = elem.next;
elem.next = e;
e.parent = elem;
在這里,我的示例適用於雙向鏈表,而elem表示當前正在比較要添加的對象的鏈表中的元素。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.