排序算法(三):交換排序(冒泡排序、快速排序的遞歸與非遞歸)

交換排序

  • 基本思想:

所謂交換,就是根據序列中兩個元素鍵值的比較結果來交換這兩個元素在序列中的位置,將鍵值較大的元素向序列的尾部移動,鍵值較小的元素向序列的前部移動。

  • 排序分類:

交換排序分爲冒泡排序快速排序

一:冒泡排序

  • 排序過程:

根據序列中相鄰兩個元素鍵值的比較結果來對換這兩個元素在序列中的位置,將鍵值較大的元素向序列的尾部移動,鍵值較小的元素向序列的前部移動。這樣的過程十分類似於水泡上浮冒泡的過程,所以成爲冒泡排序。
在這裏插入圖片描述

  • 代碼實現:

在冒泡排序的過程中,如果某一趟執行完畢,沒有做任何一次交換操作,說明剩下的序列已經有序,排序操作也就可以完成了。

C++實現:

#include<iostream>
#include<vector>
using namespace std;

void Bubblesort(vector<int>& array){
	int sz = array.size();
	// 外層循環控制每一趟冒泡的次數
	for (int out = 0; out < sz - 1; out++){
		bool flag = true;
		// 內層循環控制單趟排序
		for (int in = 0; in < sz - out - 1; in++){
			if (array[in]>array[in + 1]){
				swap(array[in], array[in + 1]);
				flag = false;
			}
		}
		// 一趟排序中沒有進行交換說明已經有序
		if (flag == true){
			break;
		}
	}
}

int main(){
	vector<int> array = { 9, 3, 1, 4, 2, 7, 8, 6, 5 };
	Bubblesort(array);
	for (const auto& e : array){
		cout << e << " ";
	}
	cout << endl;
}

運行結果:1 2 3 4 5 6 7 8 9

C實現:

#include<stdio.h>

void Swap(int* num1, int* num2){
	int temp = *num1;
	*num1 = *num2;
	*num2 = temp;
}

void Print(int* array, int sz){
	for (int cur = 0; cur < sz; cur++){
		printf("%d ", array[cur]);
	}
	printf("\n");
}

void Bubblesort(int* array, int sz){
	// 外層循環控制每一趟冒泡的次數
	for (int out = 0; out < sz - 1; out++){
		int flag = 0;
		// 內層循環控制單趟冒泡
		for (int in = 0; in < sz - out - 1; in++){
			if (array[in]>array[in + 1]){
				Swap(&array[in], &array[in + 1]);
				flag = 1;
			}
		}
		if (flag == 0){
			break;
		}
	}
}

int main(){
	int array[] = { 9, 3, 1, 4, 2, 7, 8, 6, 5 };
	int sz = sizeof(array) / sizeof(array[0]);
	Bubblesort(array, sz);
	Print(array, sz);
}

運行結果:1 2 3 4 5 6 7 8 9 
  • 特性總結:

1.時間複雜度:O(N2
2.空間複雜度:O(1)
3.冒泡排序是一種穩定的排序算法

二:快速排序

  • 排序過程:

任取待排序序列的中的某元素爲基準值,遍歷整個數組區間,比基準值小的元素放到基準值的左邊,比基準值大的元素放到基準值的右邊,再用相同的方式處理左右兩個無序子區間,直到小區間有序。

注意:快排在序列已經有序的情況下會很慢(時間複雜度爲:O(N^2)

在序列已經有序的情況下,如果選取第一個元素或最後一個元素爲基準值,則快速排序將會很慢。

所以基準值的選取非常的重要

三數取中法選取基準值(防止極端情況): 取左端、中間、右端三個數進行排序,將中間數作爲基準值。

  • 代碼實現(遞歸):

C++實現前後指針法版本的快速排序

#include<iostream>
#include<vector>
using namespace std;

int GetMiddle(vector<int>& array, int begin, int end){
	if (array.size() >= 3){
		// 獲取中位數下標
		int mid = (begin + end) >> 1;
		// 三數比較
		if (array[begin] < array[mid]){
			if (array[mid] < array[end]){
				// begin < mid < end
				return mid;
			}
			else if (array[begin] < array[end]){
				// mid > end > begin
				return end;
			}
			else{
				// mid > begin > end
				return begin;
			}
		}
		else{
			// begin > mid
			if (array[end] < array[mid]){
				// begin > mid > end
				return mid;
			}
			else if (array[begin]>array[end]){
				// begin > end > mid
				return end;
			}
			else{
				// end > begin > mid 
				return begin;
			}
		}
	}
	else{
		if (array[begin] > array[end]){
			swap(array[begin], array[end]);
		}
		return 0;
	}
}

// 前後指針
int Partsort(vector<int>& array, int begin, int end){
	// 基準值
	int midindex = GetMiddle(array, begin, end);
	swap(array[end], array[midindex]);

	int cur = begin;
	int prev = begin - 1;

	while (cur < end){
		// 找到小於基準值的元素
		// 兩個指針一起走,若碰到大於基準值的元素前指針停下,後指針繼續++
		if (array[cur] < array[end] && ++prev != cur){
			swap(array[cur], array[prev]);
		}
		cur++;
	}

	prev++;
	// 和基準值交換
	swap(array[prev], array[end]);
	return prev;
}

void Quicksort(vector<int>& array, int begin, int end){
	if (end - begin < 1){
		return;
	}

	int keyindex = Partsort(array, begin, end);
	Quicksort(array, begin, keyindex - 1);
	Quicksort(array, keyindex + 1, end);
}

int main(){
	vector<int> array = { 4, 5, 7, 8, 1, 2, 3, 6 };
	Quicksort(array, 0, array.size()-1);

	for (const auto& e : array){
		cout << e << " ";
	}
	cout << endl;
}

運行結果:1 2 3 4 5 6 7 8
  • 特性總結:

1.時間複雜度:O(N×logN)
2.空間複雜度:O(logN)
3.快速排序是一種不穩定的排序算法

  • 代碼實現(非遞歸):

利用棧模擬遞歸的過程

#include<iostream>
#include<stack>
#include<vector>
using namespace std;

int GetMiddle(vector<int>& array, int begin, int end){
	if (array.size() >= 3){
		// 獲取中位數下標
		int mid = (begin + end) >> 1;
		// 三數比較
		if (array[begin] < array[mid]){
			if (array[mid] < array[end]){
				// begin < mid < end
				return mid;
			}
			else if (array[begin] < array[end]){
				// mid > end > begin
				return end;
			}
			else{
				// mid > begin > end
				return begin;
			}
		}
		else{
			// begin > mid
			if (array[end] < array[mid]){
				// begin > mid > end
				return mid;
			}
			else if (array[begin]>array[end]){
				// begin > end > mid
				return end;
			}
			else{
				// end > begin > mid 
				return begin;
			}
		}
	}
	else{
		if (array[begin] > array[end]){
			swap(array[begin], array[end]);
		}
		return 0;
	}
}

// 前後指針
int Partsort(vector<int>& array, int begin, int end){
	// 基準值
	int midindex = GetMiddle(array, begin, end);
	swap(array[end], array[midindex]);

	int cur = begin;
	int prev = begin - 1;

	while (cur < end){
		// 找到小於基準值的元素
		// 兩個指針一起走,若碰到大於基準值的元素前指針停下,後指針繼續++
		if (array[cur] < array[end] && ++prev != cur){
			swap(array[cur], array[prev]);
		}
		cur++;
	}

	prev++;
	// 和基準值交換
	swap(array[prev], array[end]);
	return prev;
}

// 遞歸可能會導致棧溢出
// 快排換用棧模擬實現非遞歸
void QuicksortNonr(vector<int>& array, int begin, int end){
	if (end - begin < 1){
		return;
	}

	stack<int> st;
	st.push(begin);
	st.push(end);

	while (!st.empty()){
		int right = st.top();
		st.pop();
		int left = st.top();
		st.pop();

		int keyindex = Partsort(array, left, right);
		if (left < keyindex - 1){
			st.push(left);
			st.push(keyindex - 1);
		}
		if (keyindex + 1 < end){
			st.push(keyindex + 1);
			st.push(end);
		}

	}
}

int main(){
	vector<int> array = { 4, 5, 7, 8, 1, 2, 3, 6 };
	QuicksortNonr(array, 0, array.size() - 1);

	for (const auto& e : array){
		cout << e << " ";
	}
	cout << endl;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章