冒泡排序
冒泡排序的原理如下,從第一個元素開始,把當前元素和下一個索引元素進行比較。如果當前元素大,那麼就交換位置,重複操作直到比較到最後一個元素,那麼此時最後一個元素就是該數組中最大的數。下一輪重複以上操作,但是此時最後一個元素已經是最大數了,所以不需要再比較最後一個元素,只需要比較到
length - 1
的位置。以此類推,每一次最外層循環都會將後面的數排好序,因此裏面的循環可以減少i次循環.冒泡排序算法時間複雜度爲O(n*n)
//算法實現
function bubble(array){
let len = array.length;
if(len <= 0){return false;}
for(let i = 0;i < len;i++){
//由於後面的數已經排好序了,因此第二次沒必要再循環len次,而是len-i次
for(let j = 0; j < len - 1 - i;j++){
if(array[j] > array[j+1]){
[array[j], array[j+1]] = [array[j+1], array[j]];
}
}
}
}
選擇排序
選擇排序的原理如下:先將一個值的索引設置爲最小索引minIndex,通常設置是第一個值,然後將最小索引的值與其他值進行比較,若最小索引值比其他的值要大,則將最小索引設置爲該值的下標,然後最小索引的值與該值交換。
選擇排序的時間複雜度是O(n*n)
function selection(array){
let len = array.length;
if(len <= 0){return false;}
for(let i = 0; i < len; i++){
let minIndex = i;
for(let j = i + 1;j < len;j++){
//索引的互換,minIndex可能會多次變化
minIndex = array[j] < array[minIndex] ? j : minIndex;
}
//值的互換
[array[i], array[minIndex]] = [array[minIndex], array[i]];
}
}
插入排序
插入排序的原理:默認第一個元素已經排好序,外層從第二個元素開始循環,裏層循環從第一個元素開始,若裏層循環滿足條件,則交換值。裏層循環將會進行多次比較
插入排序的時間複雜度爲O(n*n)
function insert(array){
let len = array.length;
if(len<=0){return false;}
for(let i = 1; i < len; i++){
for(let j = i - 1; j >= 0 && array[j] > array[j+1]; j--){
[array[j], array[j+1]] = [array[j+1], array[j]];
}
}
}
歸併排序
歸併排序的原理如下。遞歸的將數組兩兩分開直到最多包含兩個元素,然後將數組排序合併,最終合併爲排序好的數組。假設我有一組數組
[2,7,5,6,1,4,3]
,中間數索引是 3,先排序數組[2,7,5,6]
。在這個左邊數組上,繼續拆分直到變成數組包含兩個元素(如果數組長度是奇數的話,會有一個拆分數組只包含一個元素)。然後排序數組[2,7]
和[5,6]
,然後再排序數組[2,7,5,6]
,這樣左邊數組就排序完成,然後按照以上思路排序右邊數組,最後將數組[2, 7, 5, 6]
和[1, 3, 4]
排序。歸併排序的時間複雜度爲O(N*logN)
function sort(array){
let len = array.length;
if(len <=0){return;}
mergeSort(array, 0, len - 1);
}
function mergeSort(array, left, right){
if(left === right) {return;}
//位運算更安全,不會溢出,相當於left+(right-left)/2
let mid = parseInt(left + ((right - left) >> 1));
mergeSort(array, left, mid);
mergeSort(array, mid+1, right);
let temp = [], i = 0, p1 = left, p2 = mid+1;
while(p1 <= mid && p2 <= right){
temp[i++] = array[p1] < array[p2] ? array[p1++] : array[p2++];
}
while(p1 <= mid){
temp[i++] = array[p1++];
}
while(p2 <= right){
temp[i++] = array[p2++];
}
for(let i = 0; i < temp.length; i++){
array[left + i] = temp[i];
}
return array;
}
快速排序
快排的原理如下。隨機選取一個數組中的值作爲基準值,如最右側的值,從左至右取值與基準值對比大小。比基準值小的放數組左邊,大的放右邊,對比完成後將基準值和第一個比基準值大的值交換位置。然後將數組以基準值的位置分爲兩部分,繼續遞歸以上操作
快速排序的時間複雜度爲O(N*logN)
function quickSort(array){
let len = array.length;
if(len < 0){return;}
quick(array, 0, array.length-1);
}
function quick(array, left, right){
let index;
if(array.length > 1){
index = partition(array, left, right);
if(left < index - 1){
quick(array, left, index - 1);
}
if(index < right){
quick(array, index, right);
}
}
}
function partition(array, left, right){
let pivot = array[Math.floor((right + left) / 2)]; //選取中間的元素作爲主元
let i = left, j = right;
while(i <= j){
while(array[i] < pivot){
i++;
}
while(array[j] > pivot){
j--;
}
if(i <= j){
[array[i], array[j]] = [array[j], array[i]];
i++;
j--;
}
}
return i;
}
堆排序
堆也可以作爲一種 數據結構,通常情況下,堆被嵌入到儲存數列的排序中,並且只通過交換數字來完成排序
堆排序利用了二叉堆的特性來做,二叉堆通常用數組表示,並且二叉堆是一顆完全二叉樹(所有葉節點(最底層的節點)都是從左往右順序排序,並且其他層的節點都是滿的)
function heapSort(array) {
function maxHeapify(array, index, heapSize) {
let iMax, iLeft, iRight;
while (true) {
iMax = index;
iLeft = 2 * index + 1;
iRight = 2 * (index + 1);
if (iLeft < heapSize && array[index] < array[iLeft]) {
iMax = iLeft;
}
if (iRight < heapSize && array[iMax] < array[iRight]) {
iMax = iRight;
}
if (iMax !== index) {
//swap(array, iMax, index);
[array[iMax], array[index]] = [array[index], array[iMax]];
index = iMax;
} else {
break;
}
}
}
function buildMaxHeap(array) {
let i, iParent = Math.floor(array.length / 2) - 1;
for (i = iParent; i >= 0; i--) {
maxHeapify(array, i, array.length);
}
}
function sort(array) {
buildMaxHeap(array);
for (let i = array.length - 1; i > 0; i--) {
//swap(array, 0, i);
[array[0], array[i]] = [array[i], array[0]];
maxHeapify(array, 0, i);
}
return array;
}
return sort(array);
}