常見排序算法彙總

常言道:溫故而知新。最近有空複習了一下排序算,這裏將各種基本排序算法實現總結了一遍,並整理到了一個類中,方便以後溫習。

各種排序實現,CSortUtils類文件:

 

#ifndef _SORT_UTILS_H_
#define _SORT_UTILS_H_
#include <vector>
using namespace std;

template<class T>
class CSortUtils
{
public:
	CSortUtils();
	virtual ~CSortUtils();
	typedef struct node{
		T key;
		struct node *lChild, *rChild;
		node(T value) :key(value),lChild(NULL), rChild(NULL){}
	}Node,*BST;
public:
	void SelectSort(T *arrays, int size);
	void BubbleSort(T *arrays,int size);
	void InsertSort(T *arrays, int size);
	void ShellSort(T *arrays, int size);
	void QuickSort(T *arrays,int left,int right);
	void MergeSort(T *arrays, int left, int right);	
	void HeapSort(T *arrays, int size);
	void BSTreeSort(T *arrays, int size);
        void CountSort(int *arrays, size_t size);//計數排序,適用於無符號整型
private:
	void Swap(T &a,T &b);
	void Merge(T *arrays, int left, int right);
	void HeapAdjust(T *arrays,int pAdjust, int size);
	void CreateBST(BST &N, T *arrays, int size);
	void BSTInsert(BST &N,T value);
	void InOrderTraverse(BST N, vector<T>& temp);
	void ReleaseTreeNode(BST N);
};

template<class T>
CSortUtils<T>::CSortUtils()
{
}

template<class T>
CSortUtils<T>::~CSortUtils()
{
}

template<class T>
void CSortUtils<T>::Swap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

/*
排序過程:給定一個數值集合,循環遍歷集合,
每次遍歷從集合中選擇出最小或最大的放入集
合的開頭或結尾的位置,下次循環從剩餘的元
素集合中遍歷找出最小的並如上操作,最後直
至所有原集合元素都遍歷完畢,排序結束。
分析總結:選擇排序時間複雜度比較高,達到了O(n^2),
每次選擇都要遍歷一遍無序區間。選擇排序對一類重要的
元素序列具有較好的效率,就是元素規模很大,而排序碼
卻比較小的序列。另外要說明的是選擇排序是一種不穩定
的排序方法。
*/
template<class T>
void CSortUtils<T>::SelectSort(T *arrays, int size)
{
	int nIndex;//記錄最值索引
	for (int i = 0; i < size;++i)
	{
		nIndex = i;
		for (int j = i + 1; j < size;++j)
		{
			if (arrays[nIndex] > arrays[j])
			{
				nIndex = j;
			}
		}

		if (nIndex != i)
		{
			Swap(arrays[nIndex],arrays[i]);
		}
	}
}

/*
排序過程:冒泡排序的過程形如其名,就是依次比較相鄰
兩個元素,優先級高(或大或小)的元素向後移動,直至
到達序列末尾,無序區間就會相應地縮小。下一次再從無
序區間進行冒泡操作,依此循環直至無序區間爲1,排序結束。

分析總結:冒泡排序的時間複雜度也比較高,達到O(n^2),
每次遍歷無序區間都將優先級高的元素移動到無序區間的末
尾。冒泡排序是一種穩定的排序方式。
*/
template<class T>
void CSortUtils<T>::BubbleSort(T *arrays, int size)
{
	for (int i = 0; i < size;++i)
	{
		for (int j = 1; j < size - i;++j)
		{
			if (arrays[j]<arrays[j-1])
			{
				Swap(arrays[j],arrays[j - 1]);
			}
		}
	}
}

/*
排序過程:將前面的區間(初始區間爲1,包含第一個元素)視作有序區間,
然後將有序區間的後一元素插入到前面有序區間的適當位置。直至有有序區
間擴展到原區間的大小,排序結束。
分析總結:插入排序的時間複雜度達到O(n^2),排序的運行時間和待排序
元素的原始排列順序密切相關。插入排序是一種穩定的排序方法。
*/
template<class T>
void CSortUtils<T>::InsertSort(T *arrays, int size)
{
	for (int i = 1; i < size;++i)
	{
		for (int j = i; j>0;--j)//將第i個元素插入前面有序序列中
		{
			if (arrays[j]<arrays[j-1])
			{
				Swap(arrays[j],arrays[j - 1]);
			}
		}
	}
}

/*
將無序數組分割爲若干個子序列,子序列不是逐段分割的,而是相隔特定的增量的子序列,
對各個子序列進行插入排序;然後再選擇一個更小的增量,再將數組分割爲多個子序列進
行排序......最後選擇增量爲1,即使用直接插入排序,使最終數組成爲有序.
希爾排序Shell Sort是基於插入排序的一種改進,增量的取值規則爲第一次取總長度的一半,
第二次取一半的一半,依次累推直到1爲止.
分析總結:希爾排序的時間複雜度爲O(n的3/2次方),是一種不穩定的排序算法。
*/
template<class T>
void CSortUtils<T>::ShellSort(T *arrays, int size)
{	
	for (int nStep = size / 2; nStep > 0; nStep /= 2)//取步長
	{
		//從第nStep個元素,逐個對其所在組進行直接插入排序操作
		for (int i = nStep; i<size; i++)
		{
			int j = i;
			while (j - nStep >= 0 && arrays[j]<arrays[j - nStep])
			{
				//插入排序採用交換法
				Swap(arrays[j], arrays[j - nStep]);
				j -= nStep;
			}
		}
	}
}

/////////////////////////////////////////////////////////////////////////////////////////////////
/*
排序過程:快速排序應該是應用最廣泛的排序算法,它是採用了分治的思想(這種思想很重要)。
其基本的思想就是任取待排序序列中的某個元素(元素的選取方式在一定程序上會影響實現過程和排序效率)作爲標杆,
將待排序序列劃分爲左右兩個子序列,左側元素小於標杆元素,右側元素大於標杆元素,標杆元素則排在這兩個子序列的中間,
然後再對這兩個子序列重複上述的方法,直至排序結束。
分析總結:快速排序的時間複雜度爲O(nlogn),是一種不穩定的排序算法。
*/
template<class T>
void CSortUtils<T>::QuickSort(T *arrays, int left, int right)
{
	if (left < right)
	{
		int i = left - 1;
		int j = right + 1;
		T mid = arrays[(left + right) / 2];//標杆元素
		while (true)
		{
			while (arrays[++i] < mid);
			while (arrays[--j] > mid);
			if (i >= j)
			{
				break;
			}
			Swap(arrays[i], arrays[j]);
		}
	
		QuickSort(arrays, left, i - 1);
		QuickSort(arrays, j + 1, right);
	}
}

/*
排序過程:歸併排序的原理比較簡單,也是基於分治思想的。
它將待排序的元素序列分成兩個長度相等的子序列,然後爲
每一個子序列排序,然後再將它們合併成一個序列。
分析總結:歸併排序最好、最差和平均時間複雜度都是O(nlogn),
是一種穩定的排序算法。
*/
template<class T>
void CSortUtils<T>::Merge(T *arrays, int left, int right)
{
	T *temp = new T[right-left+1];
	int mid = (left + right) / 2;
	int i = left, j = mid + 1, m = 0;
	while (i <= mid && j<= right)
	{
		if (arrays[i]>arrays[j])//將小的放前面
		{
			temp[m++] = arrays[j++];
		}
		else
		{
			temp[m++] = arrays[i++];
		}
	}

	while (i<=mid)//將多餘的元素直接存入
	{
		temp[m++] = arrays[i++];
	}

	while (j <= right)//將多餘的元素直接存入
	{
		temp[m++] = arrays[j++];
	}

	for (int n = left, m = 0; n <= right;++n,++m)
	{
		arrays[n] = temp[m];
	}

	delete temp;
}

template<class T>
void CSortUtils<T>::MergeSort(T *arrays, int left, int right)
{
	if (left < right)
	{
		int mid = (left+right) / 2;
		MergeSort(arrays,left,mid);//左
		MergeSort(arrays,mid+1,right);//右
		Merge(arrays, left, right);
	}
}

/*
排序過程:堆排序的過程分爲兩個步驟,第一步是根據初始輸入數據,建立一個初始堆;
第二步是將堆頂元素與當前無序區間的最後一個元素進行交換,然後再從堆頂元素開始對堆進行調整。
分析總結:堆排序的算法時間複雜度爲O(nlogn),它是一種不穩定的排序算法。
*/
template<class T>
void CSortUtils<T>::HeapAdjust(T *arrays, int pAdjust, int size)
{
	int pos = pAdjust;//父節點
	while (2*(pos+1)<size)
	{
		int lChild = 2 * pos + 1;
		if (arrays[lChild]>arrays[pos])//左孩子比父節點大
		{
			pos = lChild;
		}
		int rChild = lChild + 1;
		if (rChild<size&&arrays[rChild]>arrays[pos])//右孩子更大
		{
			pos = rChild;
		}

		if (pos != pAdjust)
		{
			Swap(arrays[pAdjust], arrays[pos]);
			pAdjust = pos;
		}
		else
		{
			break;
		}
	}
}

template<class T>
void CSortUtils<T>::HeapSort(T *arrays, int size)
{
	//建堆
	int pos = size / 2;
	for (int i = pos - 1; i >= 0;--i)
	{
		HeapAdjust(arrays,i,size);
	}
	//排序
	for (int j = size - 1; j > 0;)
	{
		Swap(arrays[0], arrays[j]);		
		HeapAdjust(arrays, 0, j);
		j--;
	}
}

/*
排序過程:排序樹算法應用了AVL樹的原理,只不過排序樹不是平衡的,
它的特點就是父結點元素總是比左孩子元素要大卻比右孩子元素要小。
根據這個特點,可以將原數組元素組織成排序樹,然後在對排序樹進行
中序遍歷,中序遍歷的結果就是排好序的序列。
分析總結:算法中排序樹建立的時間複雜度是O(nlogn),中序遍歷的
時間複雜度是O(n),故排序樹排序的時間複雜度爲O(nlogn)
*/
template<class T>
void CSortUtils<T>::CreateBST(BST &N, T *arrays, int size)
{
	N = NULL;
	for (int i = 0; i < size;++i)
	{
		BSTInsert(N,arrays[i]);
	}
}

template<class T>
void CSortUtils<T>::BSTInsert(BST &N, T value)
{
	if (NULL == N)
	{
		N = new Node(value);
		return;
	}

	if (value <= N->key)//
	{
		return BSTInsert(N->lChild, value);
	}

	return BSTInsert(N->rChild, value);
}

template<class T>
void CSortUtils<T>::InOrderTraverse(BST N, vector<T>& temp)
{
	if (NULL != N)
	{
		InOrderTraverse(N->lChild, temp);
		temp.push_back(N->key);
		//printf("%d,", N->key);
		InOrderTraverse(N->rChild, temp);
	}
}

template<class T>
void CSortUtils<T>::BSTreeSort(T *arrays, int size)
{
	BST N = NULL;

	CreateBST(N,arrays,size);
	vector<T> temp;
	temp.reserve(size);
	InOrderTraverse(N, temp);
	//int nLen = size;
	for (int i = 0; i < size;++i)//升序
	{
		arrays[i] = temp[i];
	}

	ReleaseTreeNode(N);
}

template<class T>
void CSortUtils<T>::ReleaseTreeNode(BST N)
{
	if (NULL != N)
	{
		ReleaseTreeNode(N->lChild);
		ReleaseTreeNode(N->rChild);
	
		delete N;
		N = NULL;
	}
}
/*
計數排序,適用於無符號整型
計數排序是一種非比較的排序算法,線性時間排序
優勢:
計數排序在對於一定範圍內的整數排序時,時間複雜度爲O(N+K)  (K爲整數在範圍)快於任何比較排序算法,因爲基於比較的排序時間複雜度在理論上的上下限是O(N*log(N))。
缺點:
計數排序是一種犧牲空間換取時間的做法,並且當K足夠大時O(K)>O(N*log(N)),效率反而不如比較的排序算法。並且只能用於對無符號整形排序。
時間複雜度:
O(N)  K足夠大時爲O(K)
空間複雜度:
O(最大數-最小數)
性能:
計數排序是一種穩定排序
*/
template<class T>
void CSortUtils<T>::CountSort(int *arrays, size_t size)
{
assert(a);
size_t max = a[0];
size_t min = a[0];
for (size_t i = 0; i < size; ++i)
{
if (a[i] > max)
{
max = a[i];
}
if (a[i] < min)
{
min = a[i];
}
}
size_t range = max - min + 1;   //要開闢的數組範圍 
size_t* count = new size_t[range];
memset(count, 0, sizeof(size_t)*range);  //初始化爲0 
//統計每個數出現的次數 
for (size_t i = 0; i < size; ++i)   //從原數組中取數,原數組個數爲size 
{
count[a[i] - min]++;
}
//寫回到原數組 
size_t index = 0;
for (size_t i = 0; i < range; ++i)  //從開闢的數組中讀取,開闢的數組大小爲range 
{
while (count[i]--)
{
a[index++] = i + min;
}
}
delete[] count;
}

 


測試代碼:

 

 

#include "SortUtils.h"
int main(int argc, char* argv[])
{

	CSortUtils<int> sorter;
	int a[] = {55,28,27,5,48,32,11,10,92,5,3};
	int nLen = 11;

	//print
	printf("排序前:");
	for (int i = 0; i < nLen; ++i)
	{
		if (0 == i)
		{
			printf("%d", a[i]);
		}
		else
		{
			printf(",%d", a[i]);
		}
	}
	printf("\n");
	//sorter.QuickSort(a,0,nLen-1);
	//sorter.SelectSort(a, 10);
	//sorter.BubbleSort(a, 10);
	//sorter.InsertSort(a, 10);
	sorter.ShellSort(a, 11);
	//sorter.MergeSort(a, 0, nLen - 1);
	//sorter.HeapSort(a, 11);
	//sorter.BSTreeSort(a, 11);
	//print
	printf("排序後:");
	for (int i = 0; i < nLen; ++i)
	{
		if (0==i)
		{
			printf("%d", a[i]);
		}
		else
		{
			printf(",%d", a[i]);
		}
		
	}


	getchar();
	return 0;
}

 

各種排序的穩定性,時間複雜度和空間複雜度

 

 

 

參考:http://www.cnblogs.com/kkun/archive/2011/11/23/2260312.html

         http://blog.csdn.net/lcj_cjfykx/article/details/43924793

 

 

 

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