選擇排序 selectionsort
插入排序 insertionsort
歸併排序 mergesort
快速排序 quicksort
總結掌握常見的排序演算法能夠讓我們更好的理解迴圈不變數,還有減而知之,分而治之的思想,更好的寫出遞迴**。
當我們需要對資料進行二次或以上排序時,穩定性這個特性才會有意義。舉個例子,我們對全年級的班級序號進行排序,然後再對所有學生的成績進行排序,如果排序演算法是穩定的,那麼假設學生a和b的成績都是95分,a排在b的前面,就說明a的班級序號在b的前面。
通過內外迴圈,交換相鄰元素,使較小的元素像泡泡一樣「浮」到陣列的前面。
氣泡排序是穩定的,因為元素的交換只發生在相鄰元素之間。舉個栗子,如果 nums[i] == nums[i + 1],應該沒人會閒得交換他倆吧。
為了更形象的記憶,我們可以想象一下泡泡上浮到水面的過程,假設有兩個大小一樣的泡泡,乙個在前乙個在後,它們上浮的速度也是一樣的,浮到水面的順序不會變。
class
solution
}// 優化:如果內迴圈沒有發生swap則提前結束排序if(
}return nums;
}public
void
swap
(int
nums,
int i,
int j)
}
像打鬥地主發牌後整理手牌一樣,第一次挑一張最小的牌,放在邊上,第二次挑剩餘手牌中最小的一張放在邊上,以此類推。
選擇排序是交換次數最少的排序演算法,每次交換的元素必然在它應該在的位置。
選擇排序是不穩定的。選擇排序基於交換,而交換兩個不相鄰的元素會破環它們和剩餘元素的相對位置。
舉個例子:(4) 3 [4] 2 1
把1與(4)交換,改變了(4)與[4]的相對位置。
package leetcode.main;
public
class
main
;selectionsort
(nums)
;for
(int n : nums)
}public
static
void
selectionsort
(int
nums)
}swap
(nums, i, minidx);}
}public
static
void
swap
(int
nums,
int i,
int j)
}
將未排序元素「插入」到已排序好的序列中,形成新的有序元素序列。
插入排序和選擇排序乍一看感覺挺相似的,但是仔細看上面的**可以發現,選擇排序是每次一口氣交換兩個元素,而選擇排序是將元素乙個乙個的挪過去的,而也就是這個「挪」使得插入排序沒有改變元素相對位置,所以插入排序是穩定的。
public
int[
]sortarray
(int
nums)
nums[j]
= nums[j -1]
;// nums[j] 被 nums[j - 1]覆蓋
j--;}
nums[j]
= temp;
// 將temp插入j位置}}
運用分而治之的思想,將序列對半分,再將對半分之後得到的序列繼續對半分,直到不能分為止(序列長度為1),然後合併。
分而治之中的"分",也就是遞迴一分為二,不會破壞元素間的相對位置。重點就看分而治之中的「治「,遇到兩個相同元素時,不會有人閒的將這倆相同元素交換位置。所以綜合而言,歸併排序是穩定的。
class
solution
public
void
mergesort
(int
nums,
int left,
int right)
int m = left +
((right - left)
>>1)
;int
temp =
newint
[right - left +1]
;// 歸併排序左半邊
mergesort
(nums, left, m)
;// 歸併排序右半邊
mergesort
(nums, m +
1, right)
;// 此時nums[left..m]和nums[m+1..right]各自已經排序
// 合併兩個排序陣列
// temp:輔助合併陣列的臨時陣列
merge
(nums, left, m, right, temp);}
/* nums:由兩半有序陣列組成的陣列
left:左半邊有序陣列的左邊界
m:左半邊有序陣列的右邊界
right:右半邊有序陣列的右邊界
temp:臨時陣列,用來有序的存放nums中元素
merge方法執行完畢後,nums[left..right]為有序陣列
*/public
void
merge
(int
nums,
int left,
int m,
int right,
int[
] temp)
else
t++;}
// 此時 r 已經到底,說明右半邊陣列已經合併完
// 把左半邊剩餘元素合併
while
(l <= m)
// 此時 l 已經到底,說明左半邊陣列已經合併完
// 把右半邊剩餘元素合併
while
(r <= right)
// temp為有序陣列,將temp元素複製給nums
通過切分(partition)將資料分割成兩部分,其中一部分的所有元素都比另一部分小,然後再切分這兩部分資料,一直重複。
切分操作會破環元素相對位置,所以快速排序不是穩定的。
class
solution
public
void
quicksort
(int
nums,
int left,
int right)
int index =
partition
(nums, left, right)
;quicksort
(nums, left, index)
;quicksort
(nums, index +
1, right);}
/* partition方法返回乙個下標lt
nums[l,lt - 1] <= nums [lt] <= nums[lt + 1, r]
*/public
intpartition
(int
nums,
int l,
int r)
i++;}
swap
(nums, pivot, lt)
;return lt;
}public
void
swap
(int
nums,
int i,
int j)
}
最差時間複雜度
平均時間複雜度
空間複雜度
穩定性氣泡排序
o(n^2)
o(n^2)
o(1)
穩定選擇排序
o(n^2)
o(n^2)
o(1)
不穩定插入排序
o(n^2)
o(n^2)
o(1)
穩定歸併排序
o(nlogn)
o(nlogn)
o(n)
穩定快速排序
o(n^2)
o(nlogn)
o(n)
不穩定這些排序演算法的具體實現不需要我們死記硬背,而是通過分析排序演算法,我們可以對遞迴,分治和迴圈不變數有更深的理解,這才是學習排序演算法的核心思想。
十大經典排序演算法
載自 排序演算法是 資料結構與演算法 中最基本的演算法之一。排序演算法可以分為內部排序和外部排序,內部排序是資料記錄在記憶體中進行排序,而外部排序是因排序的資料很大,一次不能容納全部的排序記錄,在排序過程中需要訪問外存。常見的內部排序演算法有 插入排序 希爾排序 選擇排序 氣泡排序 歸併排序 快速排...
十大經典排序演算法
不穩定排序種類為4種 快速排序 核心思想是partition操作 二分法分而治之 平均時間複雜度nlogn 希爾排序 高階版的插入排序,先把間隔較遠的子串行排序,最後間隔為1時,等同於插入排序 插入排序在序列有序時,時間複雜度常數級,所以先讓子串行總體有序,能有效降低時間複雜度 平均時間複雜度n 1...
十大經典排序演算法
常見經典排序 非線性時間比較類排序 通過比較來決定元素間的相對次序,由於其時間複雜度不能突破o nlogn 因此稱為非線性時間比較類排序。線性時間非比較類排序 不通過比較來決定元素間的相對次序,它可以突破基於比較排序的時間下界,以線性時間執行,因此稱為線性時間非比較類排序。時間複雜度 空間複雜度 穩...