1. 程式人生 > >【C++實現】基本排序演算法 插入排序——歸併排序——快速排序——堆排序

【C++實現】基本排序演算法 插入排序——歸併排序——快速排序——堆排序

/*
排序總結:(基於100w隨機數,有序數、重複數測試)
1、插入排序適合近乎有序的序列
2、歸併排序優化:(優化前       120S)
1)資料小於15時採用插入排序 10S
2)避免頻繁動態申請記憶體 memcpy(dest,src,sizeof(int)*len) 用static 變數      0.2S
缺點:需要O(N)的空間複雜度

3、快速排序優化:(優化前 如果序列完全有序,則程式直接棧溢位,如果序列為隨機數  0.7S)
1)隨機選擇樞軸 可有效避免近乎有序序列排序慢的情況 儘量不用swap使用節約一次賦值的時間    0.4S
2) 資料小於15時採用插入排序  0.2S
優點:空間複雜度O(log(n))
缺點:近乎有序時效率不高

4、堆排序 :先heapify 在從小到大排
優點:空間複雜度O(1) 原地排序
*/


#include<ctime>
inline void swap2(int &a, int &b){
	int c = a;
	a = b;
	b = c;
}

//插入排序 
void insertSort(int arr[], int lo, int hi){
	for (int i = lo; i < hi; i++){
		int e = arr[i];
		int j;
		for (j = i; j >lo && e < arr[j - 1]; j--)//判斷條件放在for內部會導致錯誤
			arr[j] = arr[j - 1];
		arr[j] = e;
	}
}
void insertSort(int arr[], int n){
	insertSort(arr, 0, n);
}

//歸併排序
void Merge(int arr[], int lo, int mi, int hi){
	int lb = mi - lo;
	int lc = hi - mi;
	int* A = arr + lo;
	static int* B = new int[10000000 / 2];//此處優化後只需0.2S
	int* C = arr + mi;
	memcpy(B, A, lb*sizeof(int));
	for (int i = 0, j = 0, k = 0; j < lb || k < lc;){
		if (j < lb && (k >= lc || B[j] < C[k])) A[i++] = B[j++];
		if (k < lc && (j >= lb || B[j] >= C[k])) A[i++] = C[k++];
	}
	//delete[] B;
}
void __mergeSort(int arr[], int lo, int hi){
	//if (hi - lo < 2) return;//100w資料需要121.2S  使用插入排序優化後只需10.1S 
	if (hi - lo <= 15) {
		insertSort(arr, lo, hi);
		return;
	}
	int mi = lo + (hi - lo) / 2;
	__mergeSort(arr, lo, mi);
	__mergeSort(arr, mi, hi);
	Merge(arr, lo, mi, hi);
}
void mergeSort(int arr[], int n){
	__mergeSort(arr, 0, n);
}
int partition(int arr[], int lo, int hi){
	swap2(arr[lo], arr[rand() % (hi - lo) + lo]);
	int e = arr[lo];
	int i = lo + 1, j = hi - 1;
	while (true)
	{
		while (i <= j && arr[i] < e) i++; // 從左向右找第一個小於x的數
		while (i <= j && arr[j] > e) j--;// 從右向左找第一個大於等於x的數		
		if (i > j) break;
		swap2(arr[i++], arr[j--]);
	}
	swap2(arr[lo], arr[j]);
	return j;
}
int partition2(int arr[], int lo, int hi){
	swap2(arr[lo], arr[rand() % (hi - lo) + lo]);
	int e = arr[lo];
	hi--;
	while (lo<hi)
	{
		while (lo < hi && arr[hi] > e) hi--;// 從右向左找第一個大於等於x的數
		if (lo < hi) arr[lo++] = arr[hi];
		while (lo < hi && arr[lo] < e) lo++; // 從左向右找第一個小於x的數
		if (lo<hi) arr[hi--] = arr[lo];
	}
	arr[lo] = e;
	return lo;
}
void quickSort(int arr[], int lo, int hi){
	//if (hi - lo < 2) return;
	if (hi - lo <= 15) {
		insertSort(arr, lo, hi);
		return;
	}
	int j = partition2(arr, lo, hi);
	quickSort(arr, lo, j);
	quickSort(arr, j + 1, hi);
}
void quickSort(int arr[], int n){
	srand(time(NULL));
	quickSort(arr, 0, n);
}

//堆排序
void shiftDown(int arr[], int k, int n){
	int e = arr[k];
	int j = k;
	while (k * 2 + 1 < n){
		j = k * 2 + 1;
		if (j + 1 < n&&arr[j + 1] > arr[j]) j++;
		if (arr[j] > e) {
			arr[k] = arr[j];
			k = j;
		}
		else break;//注意break
	}
}
void heapSort(int arr[], int n){
	for (int i = (n - 2) / 2; i >= 0; i--){
		shiftDown(arr, i, n);
	}
	for (int i = n - 1; i >= 0; i--){
		swap2(arr[0], arr[i]);
		shiftDown(arr, 0, i);
	}
}