1. 程式人生 > >基礎算法系列之排序演算法-6.折半插入排序 並用之解決hdu 1412問題

基礎算法系列之排序演算法-6.折半插入排序 並用之解決hdu 1412問題

       我們之前已經瞭解了5種基礎演算法,是否自己找了一些題練練手呢~話不多說,讓我們進入第6中基礎演算法的學習吧。本篇我們將學習又一種排序演算法——折半插入排序演算法,跟上篇我們所學習的快速排序有點像,都是建立在我們之前學習的演算法的基礎上改進而來的。從這個演算法的名字中大概就能知道它是建立在哪個演算法的基礎之上的,沒錯,就是折半(二分)查詢和直接插入排序。

折半插入排序

        我們知道,直接插入排序我們是通過順序查詢(即逐個元素逐一比較)然後找到待插入元素的位置,而我們之前學習了一種效率較高的查詢演算法——二分(折半)查詢,而且我們需要進行查詢的表又是有序的,這樣就剛好符合了二分查詢的使用條件,這樣就可以提高我們排序的效率。

折半插入排序的演算法思想

 通過將直接插入排序的順序查詢更換為效率更高的二分查詢,以提高排序演算法的效率。

折半插入排序的實現過程

       為了適應插入排序,我們需要對之前的二分查詢做一些改進。插入排序每次的有序序列的長度並不為原序列的長度,而是從長度為1(只有a[0]一個元素)到原序列的長度n。所有我們需要給二分查詢傳入兩個引數——start和end,用來表示需要查詢的範圍。其次就是二分查詢演算法的返回值。若找到了與同相等的值,返回的middle值就是我們要插入的位置,而如果原數列中沒有找到相等的值,它會返回-1,那此時我們應該如何判斷插入的位置呢?讓我們來一起探討一下吧。

假設我們需要對數列[5,3,8,6,4]進行排序,起初elements(有序序列中元素的個數)為1。

tip:有序序列中的元素用紅色標明

       現在我們需要將3插入到有序序列中,根據二分查詢演算法,它會判斷待插入值3與中間值middle(此時為5)的大小,發現比5小,所有它將pow變成middle-1,所以此時pow變為-1。發現pow<low,結束迴圈,返回-1,我們通過示意圖可以知道,我們要查詢的插入位置就是low此時在的位置(即第一個大於待插入值元素的位置)。這是有序序列中存在比待插入值大的元素,若不存在這樣的元素,我們知道應該將其插入到有序序列的末尾,此時這個位置也正好是low所在的位置(有興趣大家可以自己畫個圖分析下)。通過以上的分析,我們知道,當二分查詢返回-1時,我們應該將其改為返回low。

程式碼實現

public static void binaryInsertSort(int[] a){
		int elements=1; //記錄有序序列中的元素個數,開始只有a[0]
		for(int i = 1;i<a.length;i++){  //從待排序數列的第二個元素開始
			int res = binarySearch(a, 0, elements-1, a[i]);  //將二分查詢返回的結果儲存在res變數中
			int temp = a[i];  //將a[i]的值臨時儲存在temp中
			for(int j =elements-1;j>=res;j--){  //從有序數列的最後位置到res,逐個向後移動一個位置
				a[j+1] = a[j];
			}
			a[res] = temp;  //將a[i]的值插入到對應位置
			elements++; //加入一個元素之後,elements自增
		}

	}
	/**
	 *
	 * 改進版的二分查詢,可以控制在一定範圍內查詢value
	 * 若未找到,將返回-1改成返回low的位置即可
	 *
	 */
	public static int binarySearch (int[] a ,int start,int end,int value) {
		int low=start,middle; 				//定義區間下界並初始化為0,宣告中間位置變數middle
		int pow = end; 			    //定義區間上界並初始化為陣列的長度
		while(pow >= low){			//開始循壞,當pow<low,說明沒有需要查詢的值
			middle = (pow+low)/2;		//為middle開始賦予初值
			if(value == a[middle]){		//先判斷middle所在處的值是不是需要查詢的值
				return middle;
			}
			else if(a[middle] > value) {    //若middle處的值大於需要查詢的值,則將搜尋範圍縮小到前半部分
				pow = middle -1;
			}
			else{								//若middle處的值小於需要查詢的值,則將搜尋範圍縮小到後半部分
				low = middle +1;
			}
		}
		return low;				//若循壞結束,即pow<low,說明需要查詢的值不存在,則返回low的位置
	}

讓我們來執行測試一下我們的演算法吧

public static void main(String[] args) {
		int[] a = new int[]{5,7,9,10,25,18,19,13};
		binaryInsertSort(a);
		for(int i :a){
			System.out.print(i+"  ");
		}
	}

講到這,又到了練手的時間了,上題~

HDU 1412 {A}+{B}

Problem Description

給你兩個集合,要求{A} + {B}. 注:同一個集合中不會有兩個相同的元素.

Input

每組輸入資料分為三行,第一行有兩個數字n,m(0<n,m<=10000),分別表示集合A和集合B的元素個數.後兩行分別表示集合A和集合B.每個元素為不超出int範圍的整數,每個元素之間有一個空格隔開.

Output

針對每組資料輸出一行資料,表示合併後的集合,要求從小到大輸出,每個元素之間有一個空格隔開.

Sample Input

1 2 
1
2 3 

1 2 
1
1 2

Sample Output

1 2 3 
1 2

分析:我們可以先建立一個集合類Set,它可以自動幫我們消重,然後再將Set集合轉化為int型別的陣列,再對此陣列進行我們今天所學的折半插入順序即可。

程式碼實現:

public static void main(String[] args) {
		int n,m;  //A,B集合的元素個數
		Scanner input = new Scanner(System.in);
		System.out.println("請輸入集合A,B的元素個數:");
		do{
			n = input.nextInt();
			m = input.nextInt();
			if(n>10000 || m>10000){  //元素個數應小於10000
				System.out.println("元素個數應小於10000,請重新輸入:");
			}
			else{
				break;
			}
		}while(true);
		Set<Integer> set = new HashSet<Integer>(); //定義集合Set類,若set中有重複的元素,自動消重
		System.out.println("請輸入A的元素:");
		for(int i = 0;i<n;i++){
			set.add(input.nextInt());  //直接將A中的元素全部新增到set中
		}
		System.out.println("請輸入B的元素:");
		for(int i =0;i<m;i++){
			set.add(input.nextInt());  //將B中的元素全部新增到set中,自動消重
		}
		Object[] resultTemp = set.toArray(); //將set轉化為Object型別的陣列
		int[] result = new int[resultTemp.length]; //定義結果集
		for(int i =0;i<resultTemp.length;i++){  //將Object型別的陣列轉化為int型別
			result[i] = (int)resultTemp[i];
		}
		binaryInsertSort(result);  //進行折半插入排序
		for(int i : result){  //輸出結果
			System.out.print(i+"  ");
		}
	}

讓我們測試一下吧

總述

 我們本次有學習了一種基礎排序演算法,可以和我們之前學習的其他演算法進行比較學習,這樣學習效果更佳呢ヾ(◍°∇°◍)ノ゙

                                        關注我們,免費獲取海量java視訊學習資源