插入排序、二分法查找、合并有序数组、选择排序

插入排序

基本概念:

将未排序序列通过向有序序列从后向前扫描,找到合适的位置插入到有序序列里。

基本步骤:

  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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章