選擇排序(重溫經典算法系列)







原理簡述

默認首元素爲最(大/小)值,接着與剩餘元素逐一比較找出真正的最值元素;
中間過程的狀態描述:
序列前段部分的元素有序,後段爲待排序部分.
該假設之所以默認選擇首元素位爲最值,是爲了遍歷方便;
也可以默認最後一個元素位爲最值,甚至是任一中間元素,只不過是需要修改對待排序區間的描述
(選擇中間元素,則有可能使得簡單問題變得複雜化).


直接選擇排序算法


精煉代碼

template <typename T>
void DirectSelectionSort(T* arr, const std::size_t n) {
	assert(arr);
	for (std::size_t i = 0; i < n - 1; ++i) {
		std::size_t minIndex = i;//升序;
		for (std::size_t j = i + 1; j < n; ++j) {	
			minIndex = arr[j] < arr[minIndex] ? j : minIndex;
		}
		if (minIndex != i) {
			std::swap(arr[minIndex], arr[i]);
		}
	}
}

代碼草稿

//直接選擇排序;
//最原始默認首元素爲最(大/小)值,接着與剩餘元素逐一比較找出真正的最值元素;
template <typename T>
//void DataSort<T>::DirectSelectionSort(T* arr, const std::size_t n) {
void DirectSelectionSort(T* arr, const std::size_t n) {
	assert(arr);

	for (std::size_t i = 0; i < n - 1; ++i) {
		//for (std::size_t i = 0, j = i + 1; i < n - 1; ++i) {
		std::size_t minIndex = i;//升序時使用;
		//std::size_t maxIndex = i;//降序時使用;

		for (std::size_t j = i + 1; j < n; ++j) {
			//for ( j = i + 1; j < n; ++j) {
			if (arr[j] < arr[minIndex]) {
				//std::swap(arr[j], arr[i]);//該行代碼會導致多餘的元素交換操作,故而引入最值索引進行優化;
				minIndex = j;
			}
			//該if條件語句可修改成如下三目運算;
			//minIndex = arr[j] < arr[minIndex] ? j : minIndex;
		}
		if (minIndex != i) {
			std::swap(arr[minIndex], arr[i]);
		}
		/*std::cout << "DirectSelectionSort 當前序列: i = " << i << " j = " << j << " minIndex = " << minIndex << std::endl;
		RandomArrayFuntionHelper::PrintArray(arr, n);	*/
	}
}



雙向選擇排序算法


精煉代碼

//雙端選擇排序;
template <typename T>
void HeadTailSelectionSort(T* arr, const std::size_t n) {
	assert(arr);
	for (std::size_t i = 0, j = i + 1; i < (n >> 1); ++i) {//位運算需要括號,不分有無符號;
		std::size_t minIndex = i;//該索引往左側有序;
		std::size_t maxIndex = n - 1 - i;//該索引往右側有序(初始值狀態除外);
		if (arr[i] >= arr[n - 1 - i]) {
			std::swap(arr[i], arr[n - 1 - i]);
		}
		for (j = i; j < n - i; ++j) {
			minIndex = arr[j] < arr[minIndex] ? j : minIndex;
			maxIndex = arr[j] > arr[maxIndex] ? j : maxIndex;
		}
		if (minIndex != i) {
			std::swap(arr[minIndex], arr[i]);
		}
		if (maxIndex != n - 1 - i) {
			std::swap(arr[maxIndex], arr[n - 1 - i]);
		}
	}
}


溫馨提示:
斷言語句 assert(arr); 開頭需要include頭文件cassert(C++,即:
#include <cassert>

代碼草稿

//雙端選擇排序;
template <typename T>
//void DataSort<T>::HeadTailSelectionSort(T* arr, const std::size_t n) {
void HeadTailSelectionSort(T* arr, const std::size_t n) {
	assert(arr);

	//雙端掃描,中間部分爲無序區間,外層循環次數可減半;
	//for (std::size_t i = 0; i < n / 2; ++i) {
	////for (std::size_t i = 0, j = i + 1; i < n / 2; ++i) {
	for (std::size_t i = 0, j = i + 1; i < (n >> 1); ++i) {//位運算需要括號,不分有無符號;

		//假設最終要求數列升序,則應使得左小右大;
		//藉助 i 實現兩端索引向中間挪位,當然也可分別 ++minIndex 和 --maxIndex;
		std::size_t minIndex = i;//該索引往左側有序;
		std::size_t maxIndex = n - 1 - i;//該索引往右側有序(初始值狀態除外);

		//首先要確保最大最小匹配:存在設計漏洞即——當最小索引所在位置即是所有元素的最大值時,會導致一輪刷選結果的最大索引獲取的是第二大的值;
		//https://wenku.baidu.com/view/a6120c53bd64783e08122b2f.html;
		if (arr[i] >= arr[n - 1 - i]) { 
			std::swap(arr[i], arr[n - 1 - i]);
		}

		//for (std::size_t j = i + 1; j < n - i ; ++j) {
		for ( j = i ; j < n - i; ++j) {
			minIndex = arr[j] < arr[minIndex] ? j : minIndex;
			maxIndex = arr[j] > arr[maxIndex] ? j : maxIndex;
		}
		if (minIndex != i) {
			std::swap(arr[minIndex], arr[i]);
		}
		if (maxIndex != n - 1 - i) {
			std::swap(arr[maxIndex], arr[n - 1 - i]);
		}

		//參考文獻:https://www.doc88.com/p-6816672556940.html
		/*std::cout << "HeadTailSelectionSort 當前序列: i = " << i << " j = " << j << " minIndex = " << minIndex << " maxIndex = "<< maxIndex << std::endl;
		RandomArrayFuntionHelper::PrintArray(arr, n);*/
		
	}
}


參考材料


雙向選擇排序算法參考文章:
兩端選擇排序算法

選擇排序算法過程演示,請參考:
排序算法過程演示



交流方式
QQ —— 2636105163(南國爛柯者)
溫馨提示:
轉載請註明出處!!
2020年3月29日20:55:40

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