1. 程式人生 > 其它 >資料結構 排序(直接插入排序、起泡(冒泡)排序、簡單選擇排序、快速排序、堆排序、基數排序)

資料結構 排序(直接插入排序、起泡(冒泡)排序、簡單選擇排序、快速排序、堆排序、基數排序)

技術標籤:資料結構資料結構排序演算法c++排序

這是目錄


需要用到的結構的定義

template<typename KeyType, typename InfoType>
struct RedType
{
	KeyType key;
	InfoType otherinfo;
};

enum Status { ERROR = 0, OK = 1 };

template<typename ElemType>
struct SqList
{
	ElemType data[MAX_LIST_SIZE +
1]; int length; }; template<typename KeyType, typename InfoType> using HeapType = SqList<RedType<KeyType, InfoType>>; template<typename KeysType, typename InfoType> struct SLCell { KeysType keys[MAX_NUM_OF_KEY]; InfoType otheritems; int next; }; template<typename KeysType,
typename InfoType> struct SLList { SLCell<KeysType, InfoType>* r; int keynum, recnum; }; using ArrType = int[RADIX];

直接插入排序

將資料分為兩個部分,前面是已排好序的部分,後面是未排好序的部分。開始時假設第一個元素是已排好序的,之後每一輪都將未排好序的部分的第一個元素,與已排好序的部分的每一個元素,從後往前依次比較,不符合要求的元素就向後移一個位置,直到找到正確的位置。
時間複雜度(平均):O(n^2)
時間複雜度(最壞):O(n^2)
空間複雜度:O(1)

穩定性:穩定

template<typename KeyType,typename InfoType>
void insertSort(SqList<RedType<KeyType, InfoType>>& L)//直接插入排序
{
	for (int i = 2; i <= L.length; i++)
	{
		if (L.data[i].key < L.data[i - 1].key)
		{
			L.data[0] = L.data[i];//哨兵節點用來記錄本輪資料
			L.data[i] = L.data[i - 1];
			int j;
			for (j = i - 2; L.data[0].key < L.data[j].key; j--)
				L.data[j + 1] = L.data[j];
			L.data[j + 1] = L.data[0];
		}
	}
}

起泡排序(氣泡排序)

每一趟從頭到尾依次將相鄰的兩個元素進行比較,並根據要求交換位置,每一趟排序都可以確定一個元素到最後一個位置,直到所有元素都確定完成。同時,如果某一趟沒有發生交換,則可以認為已經排好序了,此時無需繼續排序,所以可以設定一個標誌位,用於標誌本輪是否發生交換,若未發生交換則可以直接退出排序。
時間複雜度(平均):O(n^2)
時間複雜度(最壞):O(n^2)
空間複雜度:O(1)
穩定性:穩定

template<typename KeyType, typename InfoType>
void bubbleSort(SqList<RedType<KeyType, InfoType>>& L)//起泡排序
{
	bool exchange = true;//標誌本輪是否發生交換
	for (int i = 0; i < L.length && exchange; i++)//沒發生交換可以直接退出
	{
		exchange = false;//預設沒交換
		for (int j = 1; j < L.length - i; j++)
		{
			if (L.data[j].key > L.data[j + 1].key)
			{
				exchange = true;//發生交換
				RedType<KeyType, InfoType> t;
				t = L.data[j];
				L.data[j] = L.data[j + 1];
				L.data[j + 1] = t;
			}
		}
	}
}

簡單選擇排序

將資料分為兩個部分,前面是已排好序的部分,後面是未排好序的部分。從頭開始,每一次遍歷未排好序的部分的所有元素,選擇其中最小的元素,將其放到已排好序的部分的最後。
時間複雜度(平均):O(n^2)
時間複雜度(最壞):O(n^2)
空間複雜度:O(1)
穩定性:穩定

template<typename KeyType, typename InfoType>
int selectMinKey(SqList<RedType<KeyType, InfoType>>& L, int i)//選擇從i開始到最後的最小關鍵字
{
	int min = i;
	for (int j = i + 1; j <= L.length; j++)
		if (L.data[j].key < L.data[min].key)
			min = j;
	return min;
}

template<typename KeyType, typename InfoType>
void selectSort(SqList<RedType<KeyType, InfoType>>& L)//簡單選擇排序
{
	for (int i = 1; i < L.length; i++)//從頭開始遍歷
	{
		int min = selectMinKey(L, i);//尋找最小關鍵字
		if (min != i)
		{//交換i與最小關鍵字的元素
			RedType<KeyType, InfoType> t;
			t = L.data[min];
			L.data[min] = L.data[i];
			L.data[i] = t;
		}
	}
}

快速排序

先確定一個樞軸,將所有資料分為兩個部分,左邊的比樞軸小,右邊的比樞軸大,然後再對左右兩個部分同樣進行快速排序,直到序列有序。首先選擇第一個元素作為樞軸,然後從最後一個元素開始向前,依次將每一個元素與樞軸進行比較,如果比樞軸小,則將該元素覆蓋第一個元素,再從第一個元素向後,依次將每一個元素與樞軸進行比較,如果比第一個元素大,則將該元素覆蓋上一步確定的元素,然後再從被覆蓋的元素開始向前進行搜尋與覆蓋。以此類推,每一次都將不符合要求的元素覆蓋另一邊的元素,然後從另一邊被覆蓋的元素繼續向前或向後搜尋,直到兩邊相遇,相遇的位置再由樞軸覆蓋,至此完成了一趟快速排序,然後繼續對樞軸左右兩邊分別同樣進行快速排序。
時間複雜度(平均):O(n log⁡n)
時間複雜度(最壞):O(n^2)
空間複雜度:O(log⁡n)
穩定性:不穩定

template<typename KeyType, typename InfoType>
int partition(SqList<RedType<KeyType, InfoType>>& L, int i, int j)//調整元素關於樞軸的位置並返回樞軸座標
{
	L.data[0] = L.data[i];//哨兵節點記錄樞軸資料
	KeyType pivotKey = L.data[i].key;//取最左邊為樞軸
	while (i < j)//從左右向中間掃描
	{
		while (i < j && L.data[j].key >= pivotKey)//因為取最左邊為樞軸,所以要從最右邊開始
			j--;
		L.data[i] = L.data[j];
		while (i < j && L.data[i].key <= pivotKey)
			i++;
		L.data[j] = L.data[i];
	}
	L.data[i] = L.data[0];//樞軸位置
	return i;
}

template<typename KeyType, typename InfoType>
void QSort(SqList<RedType<KeyType, InfoType>>& L, int i, int j)//遞迴進行快速排序
{
	if (i < j)
	{
		int pivotLoc = partition(L, i, j);//找到樞軸
		QSort(L, i, pivotLoc - 1);//樞軸左邊快速排序
		QSort(L, pivotLoc + 1, j);//樞軸右邊快速排序
	}
}

template<typename KeyType, typename InfoType>
void quickSort(SqList<RedType<KeyType, InfoType>>& L)//快速排序
{
	QSort(L, 1, L.length);
}

堆排序

首先要建堆,從有孩子的元素中的最後一個元素開始到第一個元素進行建堆(將該元素與自己的孩子進行比較,若不符合要求,則將自己與孩子交換,然後繼續與孩子的孩子比較,直到確定該元素的位置)。建堆完成之後,將堆分成兩部分,前面是未交換過的部分,後面是已交換過的部分,初始時,所有元素均視為未交換過。每次將第一個元素與未交換過的元素中的最後一個元素進行交換,再從第一個元素到剩下未交換過的元素中的最後一個進行建堆,直到只剩下第一個元素未交換。
時間複雜度(平均):O(n log⁡n)
時間複雜度(最壞):O(n log⁡n)
空間複雜度:O(1)
穩定性:不穩定

template<typename KeyType, typename InfoType>
void heapAdjust(HeapType<KeyType, InfoType>& H, int s, int m)//調整堆
{//在s到m範圍裡調整s到合適的位置
	RedType<KeyType, InfoType> R = H.data[s];
	int j;
	for (j = 2 * s; j <= m; j *= 2)//尋找s的後代,直到s比兩個兒子都大
	{
		if (j < m && H.data[j].key < H.data[j + 1].key)//尋找s的左右兒子中大的那個,j必須小於m,否則j+1=m+1可能影響結果
			j++;
		if (H.data[j].key <= R.key)//必須用R,不能用s,因為s可能已經與j交換過,此時s其實是存的是交換的j的值
			break;
		H.data[s] = H.data[j];
		s = j;
	}
	H.data[s] = R;
}

template<typename KeyType,typename InfoType>
void heapSort(HeapType<KeyType, InfoType>& H)//堆排序
{
	for (int i = H.length / 2; i > 0; i--)//建堆
		heapAdjust(H, i, H.length);
	for (int i = H.length; i > 1; i--)//交換第一個和最後一個元素,然後重新調整堆
	{
		RedType<KeyType, InfoType>R = H.data[1];
		H.data[1] = H.data[i];
		H.data[i] = R;
		heapAdjust(H, 1, i - 1);
	}
}

基數排序

從低位到高位,對元素的關鍵字的每一位進行分配和收集。首先進行分配,根據位將每一個元素加入到對應位的靜態連結串列中。然後進行收集,從小到大按每一個靜態連結串列的順序將元素連結起來。進行完一輪分配和收集之後繼續對關鍵字的下一位進行分配和收集,直至每一位都分配收集完成。
時間複雜度(平均):O(d(n+rd))/O(dn)
時間複雜度(最壞):O(d(n+rd))/O(dn)
空間複雜度:O(rd)
穩定性:穩定

template<typename KeysType>
KeysType ord(KeysType K, int i)//計算第i位的值
{
	KeysType t;
	while (i--)
	{
		t = K;
		t %= 10;
		K /= 10;
	}
	return t;
}

template<typename KeysType, typename InfoType>
void distribute(SLCell<KeysType, InfoType>*& r, int i, ArrType& first, ArrType& end)
{
	for (int j = 0; j < RADIX; j++)//初始化
		first[j] = 0;
	for (int p = r[0].next; p > 0; p = r[p].next)
	{
		int j = ord(r[p].keys[0], i);
		if (first[j] == 0)//還沒有元素
			first[j] = p;//作為第一個元素
		else//已經有元素
			r[end[j]].next = p;//作為下一個元素
		end[j] = p;//最後一個元素
	}
}

template<typename KeysType, typename InfoType>
void collect(SLCell<KeysType, InfoType>*& r, int i, ArrType first, ArrType end)
{
	int j;
	for (j = 0; first[j] == 0; j++);//跳過無元素的部分
	r[0].next = first[j];//設定第一個元素
	int e = end[j];//最後一個元素的下標
	while (j < RADIX - 1)
	{
		for (j++; j < RADIX - 1 && first[j] == 0; j++);//跳過無元素的部分
		if (first[j] != 0)//因為找到有元素的部分而退出迴圈
		{
			r[e].next = first[j];
			e = end[j];
		}
	}
	r[e].next = 0;
}

template<typename KeysType, typename InfoType>
void radixSort(SLList<KeysType, InfoType>& L)//基數排序
{
	ArrType first, end;
	for (int i = 1; i <= L.keynum; i++)//對每一位進行分配和收集
	{
		distribute(L.r, i, first, end);
		collect(L.r, i, first, end);
	}
}

測試

int randint(int start, int end)
{
	return rand() % (end - start) + start;
}

int main()
{
	srand((unsigned int)time(0));
	SqList<RedType<int, string>> L1, L2, L3, L4, L5;
	initSqList(L1);
	initSqList(L2);
	initSqList(L3);
	initSqList(L4);
	initSqList(L5);
	SLList<int, string> L6;

	for (int i = 1; i < randint(10, 15); i++)
	{
		RedType<int, string> t;
		t.key = randint(1, 1000);
		insertSqList(L1, t, i);
		insertSqList(L2, t, i);
		insertSqList(L3, t, i);
		insertSqList(L4, t, i);
		insertSqList(L5, t, i);
	}
	int* data = new int[L1.length];
	for (int i = 0; i < L1.length; i++)
		data[i] = L1.data[i + 1].key;
	initSLList(L6, data, L1.length, 3);

	cout << "L1 = ";
	for (int i = 1; i <= L1.length; i++)
		cout << L1.data[i].key << " ";
	cout << endl;
	cout << "L2 = ";
	for (int i = 1; i <= L2.length; i++)
		cout << L2.data[i].key << " ";
	cout << endl;
	cout << "L3 = ";
	for (int i = 1; i <= L3.length; i++)
		cout << L3.data[i].key << " ";
	cout << endl;
	cout << "L4 = ";
	for (int i = 1; i <= L4.length; i++)
		cout << L4.data[i].key << " ";
	cout << endl;
	cout << "L5 = ";
	for (int i = 1; i <= L5.length; i++)
		cout << L5.data[i].key << " ";
	cout << endl;
	cout << "L6 = ";
	for (int i = 1; i <= L6.recnum; i++)
		cout << L6.r[i].keys[0] << " ";
	cout << endl;

	cout << "L1 after insertSort = ";
	insertSort(L1);
	for (int i = 1; i <= L1.length; i++)
		cout << L1.data[i].key << " ";
	cout << endl;
	cout << "L2 after bubbleSort = ";
	bubbleSort(L2);
	for (int i = 1; i <= L2.length; i++)
		cout << L2.data[i].key << " ";
	cout << endl;
	cout << "L3 after selectSort = ";
	selectSort(L3);
	for (int i = 1; i <= L3.length; i++)
		cout << L3.data[i].key << " ";
	cout << endl;
	cout << "L4 after quickSort = ";
	quickSort(L4);
	for (int i = 1; i <= L4.length; i++)
		cout << L4.data[i].key << " ";
	cout << endl;
	cout << "L5 after heapSort = ";
	heapSort(L5);
	for (int i = 1; i <= L5.length; i++)
		cout << L5.data[i].key << " ";
	cout << endl;
	cout << "L6 after radixSort = ";
	radixSort(L6);
	for (int i = L6.r[0].next; i > 0; i = L6.r[i].next)
		cout << L6.r[i].keys[0] << " ";
	cout << endl;

	system("pause");
	return 0;
}

在這裡插入圖片描述