1. 程式人生 > >基礎算法系列之排序演算法-7.希爾排序 並解決hdu 1425問題(java實現)

基礎算法系列之排序演算法-7.希爾排序 並解決hdu 1425問題(java實現)

       我們從最初的氣泡排序演算法,到上篇文章的折半插入排序演算法,我們一共學習了5種排序演算法,相信以大家的聰明才智肯定都消化了^_^。在本篇文章中,我們又將學習第6種排序演算法——希爾排序演算法。那就讓我們直奔主題吧。

希爾排序

 讓我們回想一下直接插入排序演算法,是不是每次都是講一個待排序的元素按順序插入到一個有序序列中。那我們想,如果整個待排序的序列都是"基本有序"的,而且元素數量較少,是不是直接插入排序的執行效率會更高呢?顯然,這是肯定的,希爾排序就是通過這種思想來改進直接插入排序演算法。

希爾排序的演算法思想

希爾排序通過一個增量序列(最後一個增量必須為1),按逐個增量將待排序序列劃分為若干個組,然後對每個組中的兩個元素進行排序(第一次改進,使待排序元素數量較少),這樣通過每個增量劃分成的組通過排序之後,整體序列就成了"基本有序"了(第二次改進,使整個待排序序列整體有序),然後當增量為1時,對整體在進行一次直接插入排序,即得到了有序序列。

tip:增量是指元素間的間隔數,比如序列[4,2,3,6],4與6的增量就是3,因為間隔數為3。

 是不是有點難以理解,沒事,讓我們來個例子演示一下。

希爾排序的實現過程

例如,我們要對序列[8,6,10,13,5,7]進行希爾排序,我可以選取增量序列,d1=3,d2=1。(tip:增量的選取沒有明確的規定,筆者較喜歡奇數的增量,只要保證最後一個增量為1即可)。按第一個增量d1分組,我們可以分為3組——8與13,6與5,10與7(間隔數均為3),對每個組進行一次排序,8小於13所以8和13的位置不變;6大於5,所以6與5交換位置,得到序列[8,5,10,13,6,7];同理10大於7,交換位置得到序列[8,5,7,13,6,10]。這樣通過第一個增量排序後,我們得到了一個基本有序的數列。之後的增量重複上述操作,直到最後一個增量為1時,對整體在進行一個直接插入排序即可。

程式碼實現

public static  void shellSort (int[] a){
		int d1 = a.length/2;  //取第一個增量
		if(d1 %2 ==0){ //如果為偶數,則自減變為奇數
			d1 --;
		}
		for(int i=d1;i>=1;i=i-2){ //將第一個增量逐次減2
			if(i == 1){  //若增量為1,則對整個數列直接插入排序
				straightInsertSort(a);
				break;
			}
			for(int j=0;j<i;j++){   //每次將序列劃分為增量值的組數(例如增量為3,則分為3組),然後對每個組排序
				if(a[j]>a[j+i]){  //若前面的數大於後面的數
					a[j] = (a[j]+a[j+i]) - (a[j+i] = a[j]);  //將兩數進行交換
				}
			}
		}

	}

	public static void straightInsertSort(int[] a) {
		int elements = 1; //記錄結果集中元素的個數,起初只有a[0]這一個元素
		for(int i=1;i<a.length;i++){  //從a[1]開始逐個插入
			int j;
			for(j=0;j<elements;j++){  //遍歷結果集,逐次與a[i]作比較
				if(a[j] > a[i]){  //若a[j] >a[i],則把結果集從最後位置(即a[elements-1])到j位置逐個向後移動一個位置
					int temp = a[i]; //將要插入的值保留到temp中
					for(int k =elements-1;k>=j;k--){
						a[k+1] = a[k];
					}
					a[j] = temp;    //將要插入的值插入到j位置
					elements++; //結果集元素的個數加1
					break;
				}
			}
			if(j == elements){  //說明結果集中的元素都小於等於a[i],即將a[i]的值插入到末尾
				a[elements] = a[i];
				elements++;
			}

		}
	}

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

public static void main(String[] args) {
		int[] a = new int[]{8,10,6,19,17,3,13,16};
		shellSort(a);
		for(int i :a){
			System.out.print(i+"  ");
		}
	}

我們又來到了我們的老規矩時間,上題~

HDU 1425 sort

Problem Description

給你n個整數,請按從大到小的順序輸出其中前m大的數。

Input

每組測試資料有兩行,第一行有兩個數n,m(0<n,m<1000000),第二行包含n個各不相同,且都處於區間[-500000,500000]的整數。

Output

對每組測試資料按從大到小的順序輸出前m大的數。

Sample Input

5 3 
3 -35 92 213 -644

Sample Output

213 92 3

分析:是不是感覺so easy呢~只要我們先對這個序列進行希爾排序,然後從後往前輸出m個數即可。

程式碼實現:

public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		int n, m; // 輸入元素數量n,和輸出前m大的數中的m
		n = input.nextInt();
		m = input.nextInt();
		do {
			if (n <= 0 || n >= 1000000 || m <= 0 || m >= 1000000) {
				System.out.println("請重新輸入");
				n = input.nextInt();
				m = input.nextInt();
			}
			else{
				break;
			}
		} while (true);
		int[] a = new int[n];
		for (int i = 0; i < n; i++) { // 輸入元素
			a[i] = input.nextInt();
			if(a[i]<-500000 || a[i]>500000){
				System.out.println("請重新輸入:");
				a[i] = input.nextInt();
			}
		}
		shellSort(a);
		for (int i = n - 1; i >= n - m; i--) {
			System.out.print(a[i] + "  ");
		}
	}

讓我們來測試一下吧

總述

 本篇文章我們學習了第6種排序演算法——希爾排序,我們可以將其與之前所學習的演算法進行比較式學習,這樣學習的效率的更佳呢ヾ(◍°∇°◍)ノ゙