演算法 怒刷5大經典排序演算法之後,看看我發現了什麼

2021-10-18 20:29:02 字數 4480 閱讀 3263

選擇排序 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 因此稱為非線性時間比較類排序。線性時間非比較類排序 不通過比較來決定元素間的相對次序,它可以突破基於比較排序的時間下界,以線性時間執行,因此稱為線性時間非比較類排序。時間複雜度 空間複雜度 穩...