【排序】插入類排序—(折半)插入排序、希爾排序
前言
在資料結構和演算法中,排序是非常重要的一環,並且排序也是滲透程式設計的方方面面。
你或許在寫一個sql的order by
按照某組進行排序,又或者你在刷一道題時候、常常遇到貪心+自定義排序
求解的思路題,或者變態的面試官讓你手寫快排
,又或者是app的姓氏升降序列 - - -
然而在實際的排序演算法的實現上,方式是眾多的,不同演算法對不同的特徵資料的效率也是不同的,並且不同演算法的時間複雜度、空間複雜度也不同。
對於排序,一般認為有八大排序(也有九大)。但是在分配的大類中,我們常常分為 基於插入排序(插入排序、希爾排序);基於交換的排序(氣泡排序、快速排序);基於選擇的排序(簡單選擇排序、堆排序),歸併排序和基數排序。
插入排序
插入排序在所有排序演算法中的思想算得上是最簡單的了。和我們上學時候 從前往後、按高矮順序排序。那麼,一堆高低無序的人群中,從第一個開始,如果前面有比自己高的,就直接插入到合適的位置。一直到隊伍的最後一個完成插入整個佇列才能滿足有序。
雖然在思想
上是很簡單
的,或許你認為它的實現、次數也很簡單,每個同學插入到合適的位置就好了。但事實你這麼想錯了。你實質一眼看到確切的位置在腦海中已經默默經過pass、pass、pass。。。計算機每次只能進行一次計算,所以每個他要確定他要插入到那,他必須一個一個往前比較。所以這個時間複雜度是O(n2)的。如果資料很大的話其實效率還是很低的。
插入排序的具體步驟:
- 從第一個開始選取資料
- 從這個點和前面的比較,一直找到第一個小於自己的或者是到首位
- 插入到對應位置,重複第一步從下一個元素繼續。
在具體實現上有一下的需要注意:
- 演算法
更適合連結串列
,因為連結串列的插入刪除更簡單 - 對於陣列的插入,具體就是該元素代替被插入的位置,被插入以及後面元素全部後移一位(事實上這個順序表插入
開銷挺大
的,不太友好) - 這個插入排序如果使用陣列的話我們實質上是用陣列的多次交換而達到插入的效果。具體可以參考前面資料結構的
順序表
專欄!
至於陣列的頭節點是怎麼回事呢?因為你需要 在插入後面全部後移。我們為了減少計算,在每次比較的時候直接交換後移。用一個臨時變數儲存該元素
。而帶頭節點就是第0個不用。把第0個當作臨時空間。同時也不需要判斷是否等於0.因為0位置元素一定小於等於要插入元素。到這裡可以直接插入!
插入排序更適合連結串列,減少挪動的次數,只需考慮比較次數然後插入。
插入排序實現的程式碼為:
折半插入排序
折半插入排序和插入排序有什麼關聯?
首先,折半插入排序的本質依然是插入排序
,僅僅是對插入排序進行了部分優化。而優化的部分就是向前查詢比較的部分。其實它就是將查詢從
暴力列舉一一遍歷變為
二分查詢。
對於二分查詢,這裡不做詳細介紹,我將在另一篇博文中再做詳細介紹,因為沒進行一次插入,前面的序列都是有序的。如果序列很長的話,那麼一個個查詢比較可能會佔用過多的次數。而我們從序列中間試探二分夾逼的話可以用log級別完成查詢。序列越長那麼查詢減少的次數就越多。
當然很遺憾的是,折半插入雖然可以降低查詢次數,但是無法改變整個演算法的時間複雜度(陣列實現)。因為在每個元素的插入過程中,雖然查詢可以降到log'n
,但是在順序表的向前移動交換依然還是得一個一個移動
啊。折半插入可以理解為對於每個位置的平均時間複雜度從O(N)查詢+O(N)交換移動
變成O(logN)查詢+O(n)交換移動
。所以,整個時間複雜度依然是O(n2)
.但是,別太小看那點優化,在資料大、鏈式儲存的情況下其實還是節省很大時間的!
希爾排序
因為上述的基於排序的演算法中大部分都是適合於資料量不大、或者有序的情況才能達到較好的效果。無數牛逼的人物在不斷想方設法的優化和解決這個難題。終於,有個叫希爾的牛逼人物終於研究出來一種排序演算法——希爾排序。考慮到了資料量和有序性兩個方面緯度來設計演算法。同時,希爾排序是一組插入排序的組合。百科如下定義:
希爾排序(Shell’s Sort)是插入排序的一種又稱“縮小增量排序”(Diminishing Increment Sort),是直接插入排序演算法的一種更高效的改進版本。希爾排序是非穩定排序演算法.
希爾排序是把記錄按下標的一定增量分組,對每組使用直接插入排序演算法排序;隨著增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個檔案恰被分成一組,演算法便終止。
首先插入排序在兩個情況下效率還可以。
- 有序序列,因為如果有序的話每個元素很容易找到前面一直比自己大的幾個或0個元素,這種情況時間複雜度比現行級別稍微高那麼一丟丟。
- 元素較少。你強任你強,很少的元素再怎麼平方在計算機面前也是小菜。
而希爾排序剛好巧妙的運用,分割。利用了上面的優點。對於一個長串
,希爾首先將序列分割(非線性分割)而是按照某個數模(取餘
這個類似報數1、2、3、4. 1、2、3、4)這樣形式上在一組的分割先降低長度分別進行插入排序,這樣很小的數在後面可以通過較少的次數移動到相對考前的位置。然後慢慢合併變長,再稍稍移動。。。。。因為每次這樣小插入都會使得序列
變得更加有序
,稍微有序序列執行插入成本並不高。所以這樣能夠在合併到最終的時候基本小的在前,大的在後。這樣希爾排序下來一般還是能夠減少很多時間的。
前面的分組取餘相當於按照這個規則預處理。到最後一次就變得很有大小規則了(至少奇偶數分別有序),這樣整個插入排序的複雜度大大降低,很少出現需要移動很大幅度的數字。
雖然希爾排序看起來多了那麼幾次排序。但是在資料較長的串面前多幾次比起平方指數級別還是弟弟的。雖然希爾排序的證明是個問題,並且分割的取值也並沒有理論證明最好。但是一般從n/2一直到1.馬克思曾說:實踐是檢驗真理的唯一標準。實踐檢驗它好它就是好!
實現程式碼
package 八大排序;
import java.util.Arrays;
public class 直接插入 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int a[]= {21,25,8,7,45,2,8,18,9,88,3};
int b[]=new int[10];
a=insertsort(a);
System.out.println(Arrays.toString(a));
System.out.println();
b=shellsort(a);
System.out.println(Arrays.toString(a));
}
static int [] insertsort (int a[])
{
int team=0;
for(int i=1;i<a.length;i++)
{
System.out.println(Arrays.toString(a));
team=a[i];
for(int j=i-1;j>=0;j--)
{
if(a[j]>team)
{
a[j+1]=a[j];
a[j]=team;
}
else {
break;
}
}
}
return a;
}
static int [] shellsort (int a[])
{
int d=a.length;
int team=0;//臨時變數
for(;d>=1;d/=2)
for(int i=d;i<a.length;i++)
{
System.out.println(Arrays.toString(a));
team=a[i];
for(int j=i-d;j>=0;j-=d)
{
if(a[j]>team)
{
a[j+d]=a[j];
a[j]=team;
}
else {
break;
}
}
}
return a;
}
}
輸出結果:
[21, 25, 8, 7, 45, 2, 8, 18, 9, 88, 3]
[21, 25, 8, 7, 45, 2, 8, 18, 9, 88, 3]
[8, 21, 25, 7, 45, 2, 8, 18, 9, 88, 3]
[7, 8, 21, 25, 45, 2, 8, 18, 9, 88, 3]
[7, 8, 21, 25, 45, 2, 8, 18, 9, 88, 3]
[2, 7, 8, 21, 25, 45, 8, 18, 9, 88, 3]
[2, 7, 8, 8, 21, 25, 45, 18, 9, 88, 3]
[2, 7, 8, 8, 18, 21, 25, 45, 9, 88, 3]
[2, 7, 8, 8, 9, 18, 21, 25, 45, 88, 3]
[2, 7, 8, 8, 9, 18, 21, 25, 45, 88, 3]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
空格
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
[2, 3, 7, 8, 8, 9, 18, 21, 25, 45, 88]
最後,如果感覺可以的話歡迎點讚唄!後面持續分享!歡迎關注筆者公眾號:bigsai
,歡迎交流!
IT圈不嫌多一個朋友,筆者也希望能成為你的朋友,共同學習,共同進步!
相關文章
- 三種插入排序 直接插入排序,折半插入排序,希爾排序排序
- [java]插入排序及折半插入排序Java排序
- 排序演算法__折半插入排序排序演算法
- 排序演算法之折半插入排序排序演算法
- 【筆記】折半插入排序筆記排序
- 排序演算法 - 快速插入排序和希爾排序排序演算法
- 排序演算法(氣泡排序,選擇排序,插入排序,希爾排序)排序演算法
- #排序演算法#【2】直接插入排序、希爾排序排序演算法
- 插入排序排序排序
- 對c語言系統庫函式、堆排序、希爾排序、折半插入排序、快速排序消耗時間的比較C語言函式排序
- 排序之插入排序排序
- 排序(1)--插入排序和交換排序排序
- 插入排序排序
- C語言例題19:折半插入排序C語言排序
- 經典演算法之折半插入排序演算法排序
- 【PHP資料結構】插入類排序:簡單插入、希爾排序PHP資料結構排序
- php插入排序,快速排序,歸併排序,堆排序PHP排序
- 排序專題 -- (1)插入排序排序
- 選擇排序和插入排序排序
- 排序演算法——插入排序排序演算法
- 桶排序 選擇,插入排序排序
- 希爾排序使用直接插入排序、二分插入排序的C++程式碼實現演算法排序C++演算法
- 03 插入排序排序
- (一)氣泡排序、選擇排序、插入排序排序
- 排序演算法:插入折半排序 PHP 版排序演算法PHP
- 查詢與排序04,插入排序排序
- 氣泡排序 插入排序 快排排序
- 關於js陣列的六種演算法---水桶排序,氣泡排序,選擇排序,快速排序,插入排序,希爾排序的理解。JS陣列演算法排序
- Python八大演算法的實現,插入排序、希爾排序、氣泡排序、快速排序、直接選擇排序、堆排序、歸併排序、基數排序。Python演算法排序
- python插入排序Python排序
- 數的插入排序排序
- 插入排序(PHP,C)排序PHP
- 如何理解插入排序?排序
- PHP 排序演算法之插入排序PHP排序演算法
- 圖解選擇排序與插入排序圖解排序
- php實現 氣泡排序,插入排序,選擇排序PHP排序
- PHP 常見4種排序 氣泡排序、選擇排序、插入排序、快速排序PHP排序
- 筆試之排序-直接插入排序、氣泡排序、快速排序筆試排序