演算法基礎 --- 十大排序

帕尼尼0_0發表於2018-09-13

三大基礎排序

1.氣泡排序

1.比較相鄰的兩個元素,如果前一個比後一個大,則交換位置。
2.第一輪的時候最後一個元素應該是最大的一個。
3.按照步驟一的方法進行相鄰兩個元素的比較,這個時候由於最後一個元素已經是最大的了,所以最後一個元素不用比較。

function bubbleSort(arr) {
	for(let i=0;i<arr.length-1;i++){
		for(let j=0;j<arr.length-1-i;j++){
			if(arr[j] > arr[j+1]){
				swap(arr, j, j+1);
			}
		}
	}
}

複雜度分析

  1. 時間複雜度 O(n^2)

第一趟a1&a2、a2&a3、…an-2&an-1、an-1&an:共n-1次比較
第二趟a1&a2、a2&a3、…an-2&an-1:共n-2次比較

第n-1趟a1與a2比較:共一次比較
1+2+3+......+n-1 = 1/2(n-1)^2

  1. 空間複雜度 O(1)

動態圖

2.選擇排序

首先在未排序序列中找到最小元素,存放到排序序列的起始位置,
然後,再從剩餘未排序元素中繼續尋找最小元素,然後放到已排序序列的末尾。
以此類推,直到所有元素均排序完畢。

function selectSort(arr) {
	let minIndex;
	for(let i=0;i<arr.length-1;i++){
		minIndex = i;
		for(let j=i+1;j<arr.length;j++){
			if(arr[j] < arr[minIndex]){
				minIndex = j;
			}
		}
		swap(arr, i, minIndex);
	}
	
}

複雜度分析

  1. 時間複雜度

第一趟a2&a1、a3&a1、…an-1&a1、an&a1:共n-1次比較
第二趟a3&a2、a4&a2、…an-1&a2、an&a2:共n-2次比較

第n-1趟an與an-1比較:共一次比較
1+2+3+......+n-1 = 1/2(n-1)^2

  1. 空間複雜度 O(1)

動態圖

3.插入排序

從第一個元素開始,該元素可以認為已經被排序
2.取出下一個元素,在已經排序的元素序列中從後向前掃描
3.如果該元素(已排序)大於新元素,將該元素移到下一位置
4.重複步驟3,直到找到已排序的元素小於或者等於新元素的位置
5.將新元素插入到下一位置中
6.重複步驟2

function insertSort(arr) {
	let index;
	for(let i=0;i<arr.length-1;i++){
		index = i;
		while(index >= 0 && arr[index] > arr[index+1]){
			swap(arr, index, index+1);
			index--;
		}
	}	
}

複雜度分析

  1. 時間複雜度

最好情況(arr正有序): a1&a2、a2&a3、an-1&an:共n次比較 O(n)
最壞情況(arr逆有序): O(n^2)
第一趟a2&a1:共1次比較
第二趟a3&a2、a3&a1:共2次比較

第n-1趟an&an-1… an&a1:共n-1次比較
1+2+3+......+n-1 = 1/2(n-1)^2

  1. 空間複雜度 O(1)

動態圖

三大基礎排序中,冒泡和選擇只具有教學意義,實際開發中並不使用。

三大進階排序

1.歸併排序

歸併排序是建立在歸併操作的一種有效的排序演算法,該演算法是採用分治法的一個非常典型的應用。歸併排序是一種穩定的排序演算法,將已有序的子序列合併,等到一個完全有序的序列,即先使每個子序列有序,再使子序列段有序,若將兩個有序表合併成一個有序表,稱作2路合併

function mergeSort(arr, left, right) {
	if(arguments.length === 1){
		mergeSort(arr, 0, arr.length-1);
		return;
	}
	if(left === right)	return;
	let mid = parseInt( (left + right) / 2);
	mergeSort(arr, left, mid);
	mergeSort(arr, mid+1, right);
	merge(arr, left, mid, right);
}

function merge(arr, left, mid, right) {
	let tmp = new Array(right-left+1),
		index = 0,
		p1 = left,
		p2 = mid+1;
	while(p1 <= mid && p2 <= right) {
		tmp[index++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++]; 
	}
	while(p1 <= mid) {
		tmp[index++] = arr[p1++];
	}
	while(p2 <= right) {
		tmp[index++] = arr[p2++];
	}
	for(let i=0; i<tmp.length; i++) {
		arr[left+i] = tmp[i];
	}
}
  1. 時間複雜度

T(n) = T(n/2) * 2 + O(n) => O(nlogn)

  1. 空間複雜度:O(n)

動態圖

2.快速排序

快速排序的基本思想是:通過一趟排序將待排記錄分割成獨立的兩部分,其中一部分記錄關鍵字均比另一部分的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序

function quickSort(arr, left, right) {
	if(arguments.length === 1){
		quickSort(arr, 0, arr.length-1);
		return;
	}
	if(left >= right) return;
	let target = arr[parseInt((left+right)/2)],
		less = left-1,
		more = right+1,
		index = left;
	while(index < more) {
		if( arr[index] < target) {
			swap(arr, ++less, index++);
		} else if( arr[index] > target ) {
			swap(arr, --more, index);
		} else {
			index++;
		}
	}
	quickSort(arr, left, less);
	quickSort(arr, more, right);
}
  1. 時間複雜度

T(n) = T(n/2) * 2 + O(n) => O(nlogn)

  1. 空間複雜度:O(logn)

動態圖

3.堆排序

將初始待排序關鍵字序列(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,則整個排序過程完成。

function heapSort(array) {
	let length = array.length;
    buildMaxHeap(array);
    for (let j = length - 1; j >= 1; j--) {
        swap(array, 0, j);
        heap(array, 0, --length);
    }
}  

function buildMaxHeap(array){
	let length = array.length;
	for (let i = parseInt(length / 2) - 1; i >= 0; i--) {
	    heap(array, i, length);
	}
}
function heap(array, x, length) {
    let l = 2 * x + 1, r = 2 * x + 2, largest = x;
    if (l < length && array[l] > array[largest]) {
        largest = l;
    }
    if (r < length && array[r] > array[largest]) {
        largest = r;
    }
    if (largest != x) {
        swap(array, x, largest);
        heap(array, largest, length);
    }
}

動態圖

希爾排序,三大計數排序(計數排序、桶排序、基數排序)並不常用,這裡不做討論

相關文章