算法基礎之各種排序算法思想圖解

排序算法比較

在這裏插入圖片描述
圖片轉載於https://blog.csdn.net/weixin_41190227/article/details/86600821

排序算法穩定性

假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,r[i]=r[j],且r[i]r[j]之前,而在排序後的序列中,r[i]仍在r[j]之前,則稱這種排序算法是穩定的;否則稱爲不穩定的。

選擇排序

搞呆的,
從第1個數開始,與後面所有的數進行比較,選出最小的數排最前面。
i=0開始,比較a[i]a[i+1], 如果a[i]<a[i+1], a[i]a[i+1]交換位置, i++

圖解如下:
在這裏插入圖片描述

int sort_select(int arr[], int size) {
	int temp;
	int count_for = 0;
	for (int i = 0; i < size - 1; i++) {
		int minIndex = i;
		for (int j = i + 1; j < size; j++) {
			count_for++;
			if (arr[j] < arr[minIndex]) {
				minIndex = j;
			}
		}
		if (minIndex != i) {
			int temp = arr[i];
			arr[i] = arr[minIndex];
			arr[minIndex] = temp;
		}
	}
	return count_for;
}

測試代碼:


int  main() {
	int arr[] = { 3,9,12 ,1,6,7 };
	//int size = sizeof(arr) / sizeof(int);
	int size = sizeof(arr) / sizeof(arr[0]);

	auto start = system_clock::now();

	int count_for = 0;
	count_for = sort_select(arr, size);
	//count_for=sort_bubbling(arr, size);
	//count_for = sort_bubbling_optimize(arr, size);

	auto end = system_clock::now();
	auto duration = duration_cast<microseconds>(end - start);
	//cout <<"for循環"<<count_for<< "次花費了"<< double(duration.count()) * microseconds::period::num / microseconds::period::den<< "秒" << endl;
	cout << "for循環" << count_for << "次花費了" << double(duration.count()) << "微秒" << endl;

	for (int i = 0; i < size; i++) {
		printf("%d\n", arr[i]);
	}
	system("pause");
	return 0;
}

用數組{ 3,9,12 ,1,6,7 }測試,得到count_for(for循環次數)爲15

2層for循環,平均時間複雜度:O(n^2)

冒泡排序

從後往前,與前一個數進行比較,如果比自己大,那麼交換位置。當然也可以從前往後。

圖解如下:
2個for循環,平均時間複雜度爲O(n^2)

int sort_bubbling2(int arr[], int size) {
	//下面計算的size永遠等於1,數組做函數參數退化成指針,32位操作系統中,sizeof(任何指針變量)
	//永遠=4
	//printf("size=%d\n", sizeof(arr) / sizeof(arr[0]));
	int temp;
	int count_for = 0;
	for (int i = 0; i < size - 1; i++) {
		for (int j = size - 1; j > i; j--) {
			count_for++;
			if (arr[j] < arr[j - 1]) {
				temp = arr[j];
				arr[j] = arr[j - 1];
				arr[j - 1] = temp;
			}
		}
	}
	return count_for;
}

用數組{ 3,9,12 ,1,6,7 }測試,得到count_for(for循環次數)爲15

2層for循環,平均時間複雜度:O(n^2)

優化:

如上圖,當i=2時,也就是進行了3趟比較,就已經排好序了,通過第4趟比較,我們可以知道數組是否已經排好序,如果已經排好序,那麼不再需要進行第5趟比較。

我們可以通過定義一個boolean變量在第4趟比較完後判斷是否已經排好序,第4趟比較,flag是無法置爲true的,因爲第3趟比較已經排好序,第4趟比較不存在arr[j] < arr[j - 1]的情況,代碼如下:

int sort_bubbling3(int arr[], int size) {
	int temp;
	boolean flag;
	int count_for = 0;
	for (int i = 0; i < size - 1; i++) {
		flag = false;
		for (int j = size - 1; j > i; j--) {
			count_for++;
			if (arr[j] < arr[j - 1]) {
				temp = arr[j];
				arr[j] = arr[j - 1];
				arr[j - 1] = temp;
				flag = true;
			}
		}
		if (!flag) break;
	}
	return count_for;
}

用數組{ 3,9,12 ,1,6,7 }測試,得到count_for(for循環次數)爲14,循環次數有所減少

2層for循環,平均時間複雜度:O(n^2)

這麼看起來冒泡排序還是優於選擇排序的。

插入排序

從第2個數開始,與前面所有的數進行比較,將較小的數放前面。當與左邊最靠近的數比較時,比左邊的數大,說明左邊的數都已經排好序,應結束該趟比較,繼續下一趟比較。

圖解如下:
在這裏插入圖片描述

int sort_insert(int arr[], int size) {
	int temp;
	int count_for = 0;
	for (int i = 0; i < size - 1; i++) {
		for (int j = i + 1; j > 0; j--) {
			count_for++;
			if (arr[j] < arr[j - 1]) {
				temp = arr[j - 1];
				arr[j - 1] = arr[j];
				arr[j] = temp;
			}else {        
				break;
			}
		}
	}
	return count_for;
}

用數組{ 3,9,12 ,1,6,7 }測試,得到count_for(for循環次數)爲11,

2層for循環,平均時間複雜度:O(n^2)

希爾排序

希爾開始採用分組的思想進行排序。

在要排序的一組數中,根據間隔分爲若干子序列,並對子序列分別進行插入排序。
然後逐漸將間隔縮小,並重覆上述過程。直至間隔爲1,此時數據序列基本有序,最後進行插入排序。

研究證明:gap=length;gap=gap/3+1;性能是最好的

圖解如下:
在這裏插入圖片描述

int sort_sheer(int arr[], int size) {
	int gap = size;
	int temp;
	int count_for = 0;
	do {
		gap = gap / 3 + 1;
		for (int i = gap; i < size ; i+=gap) {
			for (int j = i - gap; j>=0; j -= gap) {
				count_for++;
				if (arr[j] > arr[j +gap]) {
					temp = arr[j + gap];
					arr[j + gap] = arr[j];
					arr[j] = temp;
				}
				else {
					break;
				}
			}
		}
	} while (gap > 1);
	return count_for;
}

用數組{ 3,9,12 ,1,6,7 }測試,得到count_for(for循環次數)爲14,

2層for循環,平均時間複雜度:O(n^2),do while裏gap計算很快歸1,可以不計入O(n)。

如果數據序列基本有序,使用插入排序會更加高效。

快速排序

分組的思想排序

先從數列中取出一個數作爲key值;
將比這個數小的數全部放在它的左邊,大於或等於它的數全部放在它的右邊;
對左右兩個小數列重複第二步,直至各區間只有1個數。

圖解如下:
在這裏插入圖片描述

int separate(int arr[], int left, int right) {
	int key = arr[left];
	while (left < right) {
		while (left < right && arr[right] >= key) {
			right--;
		}
		arr[left] = arr[right];
		while (left < right && arr[left] <= key) {
			left++;
		}
		arr[right] = arr[left];
	}
	arr[left] = key;
	return left;
}
void sort_quick(int arr[], int left, int right) {
	int median = 0;
	if (left < right) {
		median = separate(arr, left, right);
		sort_quick(arr, left, median - 1);
		sort_quick(arr, median + 1, right);
	}
}

用數組{ 3,9,12 ,1,6,7 }測試,

sort_quick(arr, 0, size - 1);

平均時間複雜度:O(N*logN)

歸併排序

將數組拆分成多個數組,每個數組僅有一個元素,然後將多個數組合併成一個數組。
合併2個數組的稱爲2路歸併,合併3個數組的稱爲3路歸併,多路歸併。

希爾排序、快速排序,平均時間複雜度均爲O(N*logN),但只有歸併排序是穩定的。

2路歸併圖解如下:
拆分:
在這裏插入圖片描述
合併:
在這裏插入圖片描述

void merge(int src[], int des[], int left, int middle, int right) {
	int i = left;
	int j = middle + 1;
	int k = left;
	while ((i <= middle) && (j <= right)) {
		if (src[i] < src[j]) {
			des[k++] = src[i++];
		}
		else {
			des[k++] = src[j++];
		}
	}
	while (i <= middle) {
		des[k++] = src[i++];
	}
	while (j <= right) {
		des[k++] = src[j++];
	}
}
void sort_merge(int src[], int des[], int left, int right, int length) {
	if (left == right) {
		des[left] = src[left];
	}
	else {
		int middle = (left + right) / 2;
		int * array = (int*)malloc(sizeof(int)*length);
		if (array != NULL) {
			sort_merge(src, array, left, middle, length);
			sort_merge(src, array, middle + 1, right, length);
			merge(array, des, left, middle, right);
		}
		free(array);
	}
}

用數組{ 3,9,12 ,1,6,7 }測試,

sort_merge(arr, arr, 0, size - 1, size);

平均時間複雜度:O(N*logN)

總結:排序算法裏比較高級的算法很多都利用了分組的思想

GitHub:https://github.com/AnJiaoDe/AlgorithmOfSort

歡迎分享、轉載、聯繫、指正、批評、撕逼

Github:https://github.com/AnJiaoDe

簡書:https://www.jianshu.com/u/b8159d455c69

CSDN:https://blog.csdn.net/confusing_awakening

ffmpeg入門教程:https://www.jianshu.com/p/042c7847bd8a

微信公衆號
這裏寫圖片描述

QQ羣

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章