常见排序算法汇总

常言道:温故而知新。最近有空复习了一下排序算,这里将各种基本排序算法实现总结了一遍,并整理到了一个类中,方便以后温习。

各种排序实现,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

 

 

 

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