2010-01-28 131 views
1

我有幾個關於插入排序實現的問題。InsertionSort與InsertionSort與BinaryInsertionSort

實現1:

public static void insertionSort(int[] a) { 
    for (int i = 1; i < a.length; ++i) { 
     int key = a[i]; 
     int j = i - 1; 

     while (j >= 0 && a[j] > key) { 
      a[j + 1] = a[j]; 
      --j; 
     } 

     a[j + 1] = key; 
    } 
} 

實現2:

public static void insertionSort(int[] a) { 
    for (int i = 1; i < a.length; ++i) { 
     for (int j = i; j > 0 && a[j - 1] > a[j]; --j) { 
      swap(a, j, j - 1); 
     } 
    } 
} 

private static void swap(int[] a, int i, int j) { 
    int tmp = a[i]; 

    a[i] = a[j]; 
    a[j] = tmp; 
} 

這是我的第一個問題:每個人都應該思考的是,第一個版本應該是快一點,第二版(因爲較少分配)但它不是(或者至少它的差異是微不足道的)。但爲什麼?其次,我想知道Java的Arrays.sort()方法也使用第二種方法(可能是因爲代碼重用,因爲swap方法在不同的地方使用,也許是因爲它更容易理解)。

實施3(binaryInsertionSort):

public static void binaryInsertionSort(int[] a) { 
    for (int i = 1; i < a.length; ++i) { 
     int pos   = Arrays.binarySearch(a, 0, i, a[i]); 
     int insertionPoint = (pos >= 0) ? pos : -pos - 1; 

     if (insertionPoint < i) { 
      int key = a[i]; 

      // for (int j = i; i > insertionPoint; --i) { 
      //  a[j] = a[j - 1]; 
      // } 
      System.arraycopy(a, insertionPoint, a, insertionPoint + 1, i - insertionPoint); 

      a[insertionPoint] = key; 
     } 
    } 
} 

是二進制插入排序任何實際用途的,或者是更加的理論嘛?在小陣列上,其他方法要快得多,而在更大的陣列上,mergesort/quicksort具有更好的性能。

+1

我猜測這種差異可以忽略不計,因爲:對於小型陣列,所花費的全部時間可以忽略不計;對於大型陣列,所花費的時間由緩存性能決定。由於第二個版本中的額外寫入與兩個版本所做的寫入相鄰,因此第二個版本不需要訪問任何額外的高速緩存行,因此性能不受影響。這只是一個猜測,但是,甚至有可能您的JIT已經將它們優化爲或多或少相同,並且如果性能存在差異,我也不會感到驚訝。 – 2010-01-28 12:34:54

回答

0
  1. 刪除虛假權利要求
  2. 比較在前兩個數目是1/2 * N(N-1),但不包括那些用於外迴路。
  3. 這些程序都不符合他們的實際工作,因爲他們沒有使用這些信息。例如,很容易在內部循環中添加一個檢查以查看是否進行了任何交換:如果沒有,那麼數組將被排序,並且您可以完成,也許可以節省大部分工作。在實踐中,這種考慮可以支配一般情況。

後記 錯過關於Java的問題:據我瞭解,Java的排序是一個相當複雜的算法,它使用了大量的特殊情況下,如專門分揀小數組的情況下,利用快速排序來完成其舉重。

+0

不,前兩個是插入排序(假設它們不是越野車)。插入排序執行n-1遍,並且在k遍之後,數組的第一個k + 1元素被排序。在給定的代碼中不可能提前退出,因爲例如最後一個元素甚至在外循環的最後一次執行時才被訪問。泡泡排序在每次傳遞中都訪問整個數組,但是沒有完全排序它留下的內容。 – 2010-01-28 12:26:54

+0

@Steve:對,是的,插入排序。不,重新佈局 - 內部循環通常會排列最大或最小值,因此不需要在隨後的遍歷中訪問整個數組,並且可以具有與簡單插入排序相同數量的比較。 – 2010-01-28 12:47:41

+0

對不起,你是對的,我只提到訪問整個數組作爲插入排序的對比,以說明爲什麼提前退出不同,而不是估計運行時間。我應該說,「冒泡排序在第一遍中訪問整個數組」,或者「冒泡排序在每次傳遞中訪問數組的整個未排序部分」等。 – 2010-01-28 13:02:28