之前把常見的那幾種算法看了好幾次,看的時候也手寫了一遍,可是等到過了
好幾天又給忘了,所以決定記錄下來分享一下,也方便自己以後查看?
冒泡排序
冒泡排序是屬於交換排序的一種,通過比較相鄰的兩個數據然後進行交
換,冒泡排序的時間複雜度爲o(n^2),空間複雜度爲o(1)。
function BubbleSort(arr) {
for(let i=0;i<arr.length;i++){
for(let j=0;j<arr.length-i-1;j++){
if(arr[j]>arr[j+1]){
[arr[j],arr[j+1]]=[arr[j+1],arr[j]]; //交換值
}
}
}
return arr;
}
選擇排序
選擇排序是通過記錄下標的方式進行比較的,定義變量(index)等於當前
下標(i),循環當前下標後面的元素,如果後面的元素比當前元素大(或者小),先不着急
交換元素,只替換下標(下標交換),一次循環過後最小元素下標的位置可以確定,最
終交換當前元素(arr[i])值和最小(或最大)下標(arr[index])的值。選擇排序的時間複雜
度爲o(n^2),空間複雜度爲o(1)。
// 選擇
function chooseSort(arr){
for(let i = 0;i<arr.length;i++){
let index = i; //記錄當前元素下標
for(let j =i+1;j<arr.length;j++){
if(arr[j]<arr[index]){
index = j; //只交換下標
}
}
[arr[i],arr[index]] = [arr[index],arr[i]] //一次循環之後再進行交換
}
return arr
}
插入排序
插入排序默認數組第一個元素有序,從第二個元素開始從後向前進行
比較,插入排序的時間複雜度爲o(n^2)。
//插入排序
function insert(arr) {
for (let i = 1; i < arr.length; i++) {
let current = arr[i]; //記錄當前值
let pre_index = i - 1; //得到上一個元素下標
while (current <= arr[pre_index] && pre_index >= 0) { //如果下標小於0,已經到達數組開頭
arr[pre_index + 1] = arr[pre_index]; //交換元素
pre_index--; //再比較下一位元素
}
arr[pre_index + 1] = current;
}
return arr;
}
以上這幾種的時間複雜度都是o(n^2),也比較耗費性能。下面介紹兩種時間復
雜度爲o(nlogn)的排序算法。
快速排序
我選擇了這種方式的快排,比較好理解。還有一種 “刨坑式”的快排,那種在理
解上有點難度,它通過左右兩個指針(或變量)對應的值進行比較,然後進行交換。
以下代碼的原理是通過數組進行排序的,遞歸的去排序左右數組。
//快排
function quickSort(arr) {
if (arr.length <= 1) return arr;
let left = [],
middle = [arr[0]],
right = [];
for (let index = 1; index < arr.length; index++) {
if (arr[index] < middle[0]) {
left.push(arr[index]) //比它小的放到左邊
}else {
right.push(arr[index]) //本身一樣大的元素也放入右邊
}
}
// 遞歸併連接,這一步很重要
return quickSort(left).concat(middle, quickSort(right))
}
歸併排序
歸併排序主要是利用分治算法的思想,將大問題化解成小問題,最終得到
原問題的解。遞歸地把數組分割成前後兩個子數組,直到數組中只有1個元素。同
時,遞歸地從兩個數組中挨個取元素,比較大小併合並。時間複雜度爲o(nlogn),空間複雜
度爲o(n)。
// 分割成只有一個元素的數組
function Split(arr){
if(arr.length<2) return arr;
let mid = Math.floor(arr.length/2); //取中間位置
let left = arr.slice(0,mid); //得到左邊數組
let right = arr.slice(mid); //得到右邊數組
return Merge(Split(left),Split(right));
}
function Merge(left,right){
// 合併+排序
var result = [];
var nl = 0;
var nr =0;
while(nl<left.length && nr<right.length){
if(left[nl] < right[nr]){//按從小到大的順序排列新的組合數組
result.push(left[nl++]);//扔進去之後迭代
}else{
result.push(right[nr++]);
}
}
while(nl<left.length){//剩下的一股腦兒扔進去。
result.push(left[nl++]);
}
while(nr<right.length){
result.push(right[nr++]);
}
return result;
}
基數排序
function radixSort(arr) {
let max_num = Math.max(...arr);
max_len = getLengthOfNum(max_num);
//根據最大數進行循環(確定循環次數)
for (let digit = 1; digit <= max_len; digit++) {
let buckets = [];
for (let i = 0; i < 10; i++) buckets[i] = []; //定義“桶”
for (let i = 0; i < arr.length; i++) {
let value= arr[i];
let pos = getSpecifiedValue(value, digit);
buckets[pos ].push(value);
}
let result = [];
buckets.toString().split(',').forEach((val) => {
if (val) result.push(parseInt(val))
})
arr = result; //改變原數組,再次排序 重要
}
return arr;
}
function getLengthOfNum(num) { return (num += '').length } //得到最大數的長度
//根據對應位置的數,如果不存在,返回0
function getSpecifiedValue(num, position) { return (num += '').split('').reverse().join('')[position - 1] || 0 }
對於基數排序可以參考基數排序詳解這篇博客,講解的比較清晰。
穩定的排序方法:冒泡排序、插入排序、基數排序、歸併排序
不穩定的排序方法:快速排序、選擇排序、堆排序、希爾排序