本博客只寫實作的思想,相關代碼地址在:gitee github
排序影片演示:排序影片演示
0、排序演算法概述
0.1 排序演算法分類
十種常見排序演算法可以分為兩大類:
- 比較類排序:通過比較來決定元素間的相對次序,由于其時間復雜度不能突破O(nlogn),因此也稱為非線性時間比較類排序,
- 非比較類排序:不通過比較來決定元素間的相對次序,它可以突破基于比較排序的時間下界,以線性時間運行,因此也稱為線性時間非比較類排序,
0.2 演算法復雜度
快速記憶上表打油詩:30秒讓你記住所有排序演算法-宋詞記憶法
.
0.3 復雜度分析遞推式
.
0.4 相關概念
- 穩定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面,
- 不穩定:如果a原本在b的前面,而a=b,排序之后 a 可能會出現在 b 的后面,
- 時間復雜度:對排序資料的總的操作次數,反映當n變化時,操作次數呈現什么規律,
- 空間復雜度:是指演算法在計算機內執行時所需存盤空間的度量,它也是資料規模n的函式,
1、冒泡排序(Bubble Sort)
冒泡排序是一種簡單的排序演算法,它重復地走訪過要排序的數列,一次比較兩個元素,如果它們的順序錯誤就把它們交換過來,走訪數列的作業是重復地進行直到沒有再需要交換,也就是說該數列已經排序完成,這個演算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端,
1.1 經典冒泡
- 比較相鄰的元素,如果第一個比第二個大,就交換它們兩個;
- 對每一對相鄰元素作同樣的作業,從開始第一對到結尾的最后一對,這樣在最后的元素應該會是最大的數;
- 針對所有的元素重復以上的步驟,除了最后一個;
- 重復步驟1~3,直到排序完成,
1.2 優化1
在經典冒泡的基礎上增加一個欄位記錄每趟是否需要交換位置,如果序列已經完全有序,可以提前終止冒泡排序
1.3 優化2
在經典冒泡的基礎上增加一個欄位記錄已經排好序的索引,如果序列尾部已經區域有序,可以記錄最后1次交換的索引,減少比較次數
2、選擇排序(Selection Sort)
選擇排序(Selection-sort)是一種簡單直觀的排序演算法,它的作業原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的末尾位置,然后,再從剩余未排序元素中繼續尋找最小(大)元素,然后放到已排序序列的前面,以此類推,直到所有元素均排序完畢,
2.1 演算法描述
- 遍歷未排序資料,比較相鄰的元素,遇到資料比已知遍歷最大數時,更新最大數的索引為當前索引
- 每趟記錄的最大數與末尾資料交換位置
- 重復1-2,直到排序完成
3、堆排序(Heap Sort)
堆排序(Heapsort)是指利用堆這種資料結構所設計的一種排序演算法,堆積是一個近似完全二叉樹的結構,并同時滿足堆積的性質:即子結點的鍵值或索引總是小于(或者大于)它的父節點,堆排序可以看作是選擇排序的一個變種
3.1 演算法描述
- 將初始待排序關鍵字序列(R1,R2….Rn)構建成大頂堆,此堆為初始的無序區;
- 將堆頂元素R[1]與最后一個元素R[n]交換,此時得到新的無序區(R1,R2,……Rn-1)和新的有序區(Rn),且滿足R[1,2…n-1]<=R[n];
- 由于交換后新的堆頂R[1]可能違反堆的性質,因此需要對當前無序區(R1,R2,……Rn-1)調整為新堆,然后再次將R[1]與無序區最后一個元素交換,得到新的無序區(R1,R2….Rn-2)和新的有序區(Rn-1,Rn),不斷重復此程序直到有序區的元素個數為n-1,則整個排序程序完成,
4、插入排序(Insertion Sort)
插入排序(Insertion-Sort)的演算法描述是一種簡單直觀的排序演算法,它的作業原理是通過構建有序序列,對于未排序資料,在已排序序列中從后向前掃描,找到相應位置并插入,
4.1 經典插入排序
- 從第一個元素開始,該元素可以認為已經被排序;
- 取出下一個元素,在已經排序的元素序列中從后向前掃描;
- 如果該元素(已排序)大于新元素,將該元素與新元素交換位置;
- 重復步驟3,直到找到已排序的元素小于或者等于新元素的位置;
- 重復步驟2~4,
4.2 優化1(交換 -> 挪動)
- 從第一個元素開始,該元素可以認為已經被排序;
- 取出下一個元素,在已經排序的元素序列中從后向前掃描;
- 如果該元素(已排序)大于新元素,將該元素移到下一位置;
- 重復步驟3,直到找到已排序的元素小于或者等于新元素的位置;
- 將新元素插入到該位置后;
- 重復步驟2~5,
4.2 優化2
利用二分搜索找到待插入位置,減少查找待插入位置次數
5、歸并排序(Merge Sort)
歸并排序是建立在歸并操作上的一種有效的排序演算法,該演算法是采用分治法(Divide and Conquer)的一個非常典型的應用,將已有序的子序列合并,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序,若將兩個有序表合并成一個有序表,稱為2-路歸并,
5.1 演算法描述
- 把長度為n的輸入序列分成兩個長度為n/2的子序列;
- 對這兩個子序列分別采用歸并排序;
- 將兩個排序好的子序列合并成一個最終的排序序列,
6、快速排序(Quick Sort)
快速排序的基本思想:通過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序,
6.1 演算法描述
快速排序使用分治法來把一個串(list)分為兩個子串(sub-lists),具體演算法描述如下:
- 從數列中挑出一個元素,稱為 “軸點”(pivot);
- 重新排序數列,所有元素比軸點值小的擺放在基準前面,所有元素比軸點值大的擺在軸點的后面(相同的數可以到任一邊),在這個磁區退出之后,該軸點就處于數列的中間位置,這個稱為磁區(partition)操作;
- 遞回地(recursive)把小于基準值元素的子數列和大于基準值元素的子數列排序,
7、希爾排序(Shell Sort)
1959年Shell發明,第一個突破O(n2)的排序演算法,是簡單插入排序的改進版,它與插入排序的不同之處在于,它會優先比較距離較遠的元素,希爾排序又叫縮小增量排序,
7.1 演算法描述
先將整個待排序的記錄序列分割成為若干子序列分別進行直接插入排序,具體演算法描述:
- 選擇一個增量序列t1,t2,…,tk,其中ti>tj,tk=1;
- 按增量序列個數k,對序列進行k 趟排序;
- 每趟排序,根據對應的增量ti,將待排序列分割成若干長度為m 的子序列,分別對各子表進行直接插入排序,僅增量因子為1 時,整個序列作為一個表來處理,表長度即為整個序列的長度,
7.2 步長序列探究
該排序的快慢取決于步長序列的選取,希爾本人提出的是(n/2,n/4,n/8...4,2,1)
目前最佳的步長序列見下圖:
.
8、計數排序(Counting Sort)
計數排序不是基于比較的排序演算法,其核心在于將輸入的資料值轉化為鍵存盤在額外開辟的陣列空間中, 作為一種線性時間復雜度的排序,計數排序要求輸入的資料必須是有確定范圍的整數,
8.1 演算法描述
- 找出待排序的陣列中最大和最小的元素;
- 統計陣列中每個值為i的元素出現的次數,存入陣列C的第i項;
- 對所有的計數累加(從C中的第一個元素開始,每一項和前一項相加);
- 反向填充目標陣列:將每個元素i放在新陣列的第C(i)項,每放一個元素就將C(i)減去1,
9、基數排序(Radix Sort)
基數排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次類推,直到最高位,有時候有些屬性是有優先級順序的,先按低優先級排序,再按高優先級排序,最后的次序就是高優先級高的在前,高優先級相同的低優先級高的在前,
9.1 演算法描述
- 取得陣列中的最大數,并取得位數;
- arr為原始陣列,從最低位開始取每個位組成radix陣列;
- 對radix進行計數排序(利用計數排序適用于小范圍數的特點);
9.2 另一個實作思路
借助桶,桶內實作排序
10、桶排序(Bucket Sort)
桶排序是計數排序的升級版,它利用了函式的映射關系,高效與否的關鍵就在于這個映射函式的確定,桶排序 (Bucket sort)的作業的原理:假設輸入資料服從均勻分布,將資料分到有限數量的桶里,每個桶再分別排序(有可能再使用別的排序演算法或是以遞回方式繼續使用桶排序進行排),
10.1 演算法描述
- 設定一個定量的陣列當作空桶;
- 遍歷輸入資料,并且把資料一個一個放到對應的桶里去;
- 對每個不是空的桶進行排序;
- 從不是空的桶里把排好序的資料拼接起來,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/43522.html
標籤:其他
上一篇:0241. Different Ways to Add Parentheses (M)
下一篇:資料結構與演算法:雙端佇列
