前言
在數據結構和算法中,排序是非常重要的一環,並且排序也是滲透編程的方方面面。
你或許在寫一個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圈不嫌多一個朋友,筆者也希望能成爲你的朋友,共同學習,共同進步!