基礎算法系列之排序演算法-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種排序演算法——希爾排序,我們可以將其與之前所學習的演算法進行比較式學習,這樣學習的效率的更佳呢ヾ(◍°∇°◍)ノ゙