插入排序、二分法查找、合併有序數組、選擇排序

插入排序

基本概念:

將未排序序列通過向有序序列從後向前掃描,找到合適的位置插入到有序序列裏。

基本步驟:

  1. 從第二個元素開始。(第一個元素默認爲是有序序列)
  2. 取出下一個元素,在已排序序列中從後向前掃描
  3. 該元素大於新元素就往後移動
  4. 重複3步驟
  5. 找到合適的位置,將新元素插入對應位置
  6. 重複1-5

例如:int[] arrays = {2,3,9,17,4,0,7,8};

  1. 有序序列:2,未排序序列 3,9,17,4,0,7,8
  2. 取出3,掃描有序序列 2,比較 3->2,3比2大,放在2,之後。
  3. 取出9,與3比較,比3大,放在3之後
  4. 取出17,與9比較,比9大,放在9之後
  5. 取出4,與17比較,比17小,將17往後移動一位
  6. 與9比較,比9小,將9往後移動一位
  7. 與3比較,比3大,將4插入3之後
  8. ……重複以上步驟………

代碼實現:

/**
* 插入排序 :將未排序序列的元素逐一和有序序列比較,並插入到有序序列中,默認第一個是有序序列
* @param array
* @return
*/
private static int[] insertSort(int[] array){
	if(array == null) return null;
	int len = array.length;
	int i,j;
	// 循環取出未排序序列元素
    for(i = 1;i < len;i++){  // c1:執行n次
    	// 取出新元素 
    	int newElement = array[i];  // c2: n-1
        // 將新元素與有序序列比較
        for(j = i;j>0 && array[j-1]>newElement;j--){ c3:
            // 元素後移
            array[j] = array[j-1];  // c4:
        }
        // 插入新元素
        array[j] = newElement; // c5:n-1 
    }
    return array; 
}
時間複雜度:
  1. 最好的情況是正序有序時,只需要比較一遍,而不需要移動元素。執行n次。
  2. 最壞的情況是逆序,需要比較一遍且每個元素都需要移動一次。

插入排序的時間複雜度是O(n^2).

二分法查找

基本概念:

將數組折半查找,比較需要查找值與中間值比較,循環縮小查找範圍,知道 找到最終數據。

/**
* 二分法查找: 將數組分成兩半,在其中一半比較需要查找的數據,依次縮小查找範圍,知道找到需要的值
* @param arrary
* @param findValue
* @return
*/
// 非遞歸方式
private static int binarySearch(int[] array,int findValue){
    if(array == null || array.lenth <= 0) return -1;
    int left = 0;
    int right = array.length - 1;
   	while(left < right){
   		int mid = (left+right)/2;
        if(findValue == array[mid]){
            return mid;
        }else if(array[mid] > findValue){
            right = mid-1;
        }else{
            left = mid +1;
        }
   	}
   	return -1;
}

// 遞歸方式
  private static int binarySearch2(int[] arrary,int findValue,int left,int right){
        if (arrary == null || arrary.length <= 0 || left > right || left <0 || right < 0) 				return -1;

        int mid = (left+right)/2;

        if (findValue > arrary[mid]){
            binarySearch2(arrary,findValue,mid,right);
        }else if (findValue < arrary[mid]){
            binarySearch2(arrary,findValue,left,mid);
        }else {
            return mid;
        }
        return -1;
 }

時間複雜度:logn.

二分法插入排序

插入排序是循環將未排序序列插入到排序序列中,在排序序列中使用二分法查找需插入的位置會比較快。

代碼實現:

/**
* 通過二分法查找進行插入排序,主要對有序序列進行二分法查找到插入的位置
* @param array
* @return
*/
private static int[] insertSortByBinary(int[] array){
    if (array == null || array.length <= 0) return null;
    int len = array.length
    for(int i =1;i< len;i++){
        // 取出新元素
        int newElement = array[i];
        int left = 0;
        int right = i-1;
        // 查找插入的位置,最終left就是插入的位置
        while(left <= right){
            int mid = (left + right)/2;
            if(array[mid]<array[i]){
                left = mid +1;
            }eles{
                right = mid -1;
            }
        }
        // 元素後移
        for(int j = i-1;j>=left;j--){
            array[j+1] = array[j];
        }
        // 插入元素
        array[left] = newElement;
    }
    return array;
}

測試代碼:

 private static int[] testArr = new int[500];
  public static void main(String[] agrs){
         Random random = new Random();
        int length = testArr.length;
        for (int i = 0; i < length; i++) {
            testArr[i] = random.nextInt(100)+3;
        }
        
        System.out.println();

        long startTime = System.currentTimeMillis();
        int[] ints = selectSort(testArr);
        long endTime = System.currentTimeMillis();
        System.out.println("選擇排序時間:"+String.valueOf(endTime - startTime));
        sprintResult(ints);
        
        
        System.out.println();
        long startTime1 = System.currentTimeMillis();
        int[] insertSort = insertSort2(testArr);
        long endTime1 = System.currentTimeMillis();
        System.out.println("直接插入法時間:"+String.valueOf(endTime1 - startTime1));
        sprintResult(insertSort);
        
        System.out.println();
        long startTime2 = System.currentTimeMillis();
        int[] insertSort1 = insertByBinarySort(testArr);
        long endTime2 = System.currentTimeMillis();
        System.out.println("二分插入法時間:"+String.valueOf(endTime2 - startTime2));
        sprintResult(insertSort1);
  }
  
  private static void sprintResult(int[] ints) {
        if (ints == null || ints.length <= 0) return;
        System.out.println();
        int length = ints.length;
        System.out.print(" 排序後: ");
        for (int i = 0; i < length; i++) {
            System.out.print("  "+ints[i]);
        }
    }

選擇排序

基本概念:

默認第一個元素是已排序並最小(大)元素,在剩餘元素中查找最小(大)的元素,與第一個交換位置;

在剩餘的元素中查找最小(大)的元素,與已經排好序的第二個元素交換位置;

重複以上步驟;

代碼實現:

 /**
 * 選擇排序:默認第一個是最小的,查找剩餘的最小的元素,交換進行排序,重複以上步驟
 * @param array
 * @return
 */
private static int[] selectSort(int[] array){
     if (array == null ) return null;
     int len = array.length;
     int tmep;
     for(int i = 0;i< len-1;i++){  // 執行n-1次
         //設定最小的元素
         int min = i;
         // 查找剩餘元素的最小元素
         for(int j = min+1;j<len;j++){
             if(array[min]>array[j]){
                 min = j;
             }
         }
         
         // 交換位置,可以在這裏控制是否是穩定排序
         if(i!=min){
             temp = array[i];
             array[i]= array[min];
             array[min]= temp;
         }
     }
     return array;
}
時間複雜度:
  1. 最好的情況是正序時,不需要交換位置,但是每次都需要查找一遍最小元素,時間複雜度O(n^2);
  2. 最壞的情況是逆序是,不但每次需要查找最小元素的位置還需要作出交換,時間複雜度O(n^2);

結論:

但需排序的數據量越大,二分法插入排序與直接插入排序時間差更明顯。

當數據量大時,選擇排序遠比插入排序耗時。

時間精準到ms。不考慮小數點。

數據量 直接插入排序耗時 二分法插入排序耗時 選擇排序耗時
100 0 0 0ms
1000 3ms 0ms 4ms
10000 12ms 5ms 57ms

合併四個有序數組

拆分爲合併兩個有序數組,再將合併好的有序數組與其他數組合並。

 /**
 * 合併2個有序數組,
 * 1、先合併兩個,然後將合併好的數組與第三個合併,然後與第四個合併
 * 2、可以使用二維數組;之後使用
 * 3、將a1和a2的每個元素進行比較,小的放進新數組裏
 * 4、將剩餘的每合併完的數組拷貝到新數組(因爲是有序數組,之前也比較過,剩餘的都是可以直接拷貝,順序不會發生變化)
 */ 
 private static int[] mergerSorted(int[] a1,int[] a2){
     if(a1== null || a2 == null) return null;
     int i = 0,j = 0,k = 0;
     int len1 = a1.length;
     int len2 = a2.length;
     int[] merge = new int[len1+len2];
     // 比較兩個不同的數組
     while(i<len1 && j<len2){
         if(a1[i]<a2[j]){
             merge[k++] = a1[i++];
         }else{
             merge[k++] = a2[j++];
         }
     }
     
     // 將剩餘的複製到新的數組裏
     while(i < len1){
         merge[k++] = a1[i++];
     }
     
     while(j<len){
         merger[k++] = a2[j++];
     }
     return merge;
 }
 
 private static int[] mergeFour(int[] a1,int[] a2,int[] a3,int[] a4){
     return mergerSorted(mergeSorted(a1,a2),mergeSorted(a3,a4));
 }

測試:

int[] a1  = {1,2,4,5,7,8,9};
int[] a2  = {5,8,10,11};
int[] a3  = {9,12,14,15};
int[] a4  = {7,9,11,16,20};
  
int[] merge = mergeFour(a1,a2,a3,a4);  
System.out.println();
sprintResult(hebin);        

測試結果:

 排序後:   1  2  4  5  5  7  7  8  8  9  9  9  10  11  11  12  14  15  16  20
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章