常用的排序算法C++

排序算法:

  • 冒泡排序
  • 堆排序
  • 归并排序
  • 快速排序
  • 希尔排序
  • 直接插入排序
  • 桶排序
  • 基数排序

冒泡排序:

有n个数,先把相邻的两个数进行比较,大的数往后挪,大的数继续和后面相邻的数比较,重复n-1次,此时,最大的数在序列的尾部,不加入第二轮排序,故第二轮比较n-2次,以此类推。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,就不需要交换,因此冒泡排序是稳定排序算法。

冒泡排序最好的情况为一次循环,则复杂度为O(n),最坏的情况需要进行所有循环,即O(n^2)。

/*
 * 冒泡排序法:O(n^2)
 */
#ifndef __BUBBLE_SORT_H__
#define __BUBBLE_SORT_H__

#include "swap.h"

/*
* 有n个数,循环i = n-1次,a[j]和a[j+1]比,如果大于则交换两个数,j++直到j < (n-i-1)退出进入下个循环。
*/
template <class ElemType>
void BubbleSort(ElemType e[], int n) {
	for (int i = 0; i < n - 1; i++) {
		for (int j = 0; j < (n - i - 1); j++) {
			if (e[j] > e[j + 1])
				Swap(e[j], e[j + 1]);
		}
	}
}

#endif // !__BUBBLE_SORT_H__

堆排序:

对n个数,先生成最大堆(或最小堆),然后把最大数放到尾部,对前面n-1个数继续进行最大堆排序,最大堆排序:从最后一个父节点开始,左右子节点做比较,大的节点与当前节点再做比较,当前节点比大的子节点小,则交换位置,继续比较倒数第二个父节点直到完成。如此循环直到完成排序。

平均时间复杂度:O(N*logN)。堆排序是不稳定的排序。

以 2, 15, 45, 76, 32, 6, 23 为例:


swap.h:

#ifndef __SWAP_H__
#define __SWAP_H__

template <class ElemType>
void Swap(ElemType &e1, ElemType &e2) {
	ElemType e;
	e = e1;
	e1 = e2;
	e2 = e;
}

#endif

heap_sort.h:
/*
* 堆排序法:O(N*logN)
*/
#ifndef __HEAP_SORT_H___
#define __HEAP_SORT_H___

#include "swap.h"

/* 1.先对整个数组进行堆排序(排序过程中先左子节点和右子节点比较,大的那个和当前节点交换)
 * 2.将排序好的最大堆的最大值与末端值调换,总共生成最大堆n-2次
 */
template <class ElemType>
void HeapSort(ElemType elem[], int n) {
	int i;
	// 第一次需要对整个数组进行堆排序(从非子叶节点(n-2)/2开始向上移动)
	for (i = (n - 2) / 2; i >= 0; i--) {
		ComposeBigestHeap(elem, i, n - 1);
	}
	// 需要进行n-2次生成最大堆
	for (i = n - 1; i > 0; i--) {
		Swap(elem[0], elem[i]);
		// 之后的每次最大堆只要对elem[0]进行最大堆生成即可(因为除了elem[0]其余均已排好序)
		// 排序范围每次均要减去最下端已拍好的数
		ComposeBigestHeap(elem, 0, i - 1);
	}
}

template <class ElemType>
void ComposeBigestHeap(ElemType elem[], int low, int high) {
	int cur, i;
	// cur为当前要生成最大堆的节点,i=2*i+1遍历向下的左子节点
	for (cur = low, i = 2 * low + 1; i <= high; i = 2 * i + 1) {
		// elem[i]为左子节点,elem[i+1]为右子节点,右子节点大的话,则指向右子节点继续向下
		if (i < high && elem[i] < elem[i + 1]) {
			i++;
		}
		// 如果当前节点值大于最大子节点,则不作后面操作
		if (elem[cur] >= elem[i]) {
			break;
		}
		Swap(elem[cur], elem[i]);
		cur = i;	// 指向交换后的子节点
	}
}

#endif // !__HEAP_SORT_H___

归并排序:

将需要作比较的数列分成两份,再对分割出来的两份进行再分割,直到无法分割为止,接着在进行逐个比较合并。时间复杂度:O(n*logn) ,属于稳定的排序。

例子:


/*
* 归并排序法:O(n*log(n))
*/
#ifndef __MERGE_SORT_H___
#define __MERGE_SORT_H___

/* 归并主要关键在于先拆分,再合并
 */
template <class ElemType>
void MergeSort(ElemType elem[], int n) {
	ElemType *telem = new ElemType[n];		// 新建一个临时数组
	SperateTwoPart(elem, telem, 0, n - 1);
	delete []telem;
}

// 将数组分成无数份,直到不可分后,比较并合并
template <class ElemType>
void SperateTwoPart(ElemType elem[], ElemType telem[], int low, int high) {
	if (low < high) {
		int mid = (low + high) / 2;

		SperateTwoPart(elem, telem, low, mid);
		SperateTwoPart(elem, telem, mid + 1, high);
		Merge(elem, telem, low, mid, high);
	}
}

template <class ElemType>
void Merge(ElemType elem[], ElemType telem[], int low, int mid, int high) {
	int i, j, k;
	for (i = low, j = mid + 1, k = low; i <= mid&&j <= high; k++) {
		// 比较,小的数值存储到临时数组中
		if (elem[i] > elem[j]) {
			telem[k] = elem[j];
			j++;
		}
		else {
			telem[k] = elem[i];
			i++;
		}
	}

	// 将归并后数组中剩余的数存储到临时数组中
	for (; i <= mid; i++, k++) {
		telem[k] = elem[i];
	}
	for (; j <= high; j++, k++) {
		telem[k] = elem[j];
	}

	// 将临时数组存回到原始数组
	for (i = low; i <= high; i++) {
		elem[i] = telem[i];
	}
}

#endif // !__MERGE_SORT_H___

快速排序:

以第一个数为基数key,所有数和key,大的放到右边,小的放到左边,再以key为中心,分成两部分。继续选择第一个数为key,按照前面的方法,直到排序完成。时间复杂度:n*log(n),属于不稳定的排序。


<pre name="code" class="cpp">/*
* 快速排序法:O(N*logN)
*/
#ifndef __QUICK_SORT_H__
#define __QUICK_SORT_H__

#include "swap.h"

/* 快速排序,在数组取一个基准数,把大于基准数的数放置在右侧,小于在左侧。
 * 再把基准数分隔开的两个部分继续取基准数分割。
 */
template <class ElemType>
void QuickSort(ElemType elem[], int n) {
	SperateQuickPart(elem, 0, n - 1);
}

template <class ElemType>
void SperateQuickPart(ElemType elem[], int low, int high) {
	if (low >= high)	return;
	// pos为序列中的基准数
	int pos = Partition(elem, low, high);
	// 以基准数为中心,左右分开
	SperateQuickPart(elem, low, pos - 1);
	SperateQuickPart(elem, pos + 1, high);
}

template <class ElemType>
int Partition(ElemType elem[], int low, int high) {
	int key = elem[low];
	while (low < high) {
		// 当左边的数小于右边,high--
		while (low < high && elem[high] >= key) {
			--high;
		}
		elem[low] = elem[high];
		while (low < high && elem[low] <= key) {
			++low;
		}
		elem[high] = elem[low];
	}
	elem[low] = key;
	// 返回基准数
	return low;
}

#endif // !__QUICK_SORT_H__
 

希尔排序:

属于稳定的排序。

(引用自百度百科):

不需要大量的辅助空间,和归并排序一样容易实现。希尔排序是基于插入排序的一种算法, 在此算法基础之上增加了一个新的特性,提高了效率。希尔排序的时间复杂度与增量序列的选取有关,例如希尔增量时间复杂度为O(n²),而Hibbard增量的希尔排序的时间复杂度为O(

   
),希尔排序时间复杂度的下界是n*log2n。希尔排序没有 快速排序算法 快 O(n(logn)),因此中等大小规模表现良好,对规模非常大的 数据排序 不是最优选择。但是比O(
   
)复杂度的算法快得多。并且希尔排序非常容易实现,算法代码短而简单。 此外,希尔算法在最坏的情况下和平均情况下执行效率相差不是很多,与此同时快速排序在最坏的情况下执行的效率会非常差。专家们提倡,几乎任何排序工作在开始时都可以用希尔排序,若在实际使用中证明它不够快,再改成快速排序这样更高级的 排序算法 . 本质上讲, 希尔排序算法 是直接插入排序算法的一种改进,减少了其复制的次数,速度要快很多。 原因是,当n值很大时 数据项 每一趟排序需要的个数很少,但数据项的距离很长。当n值减小时每一趟需要和动的数据增多,此时已经接近于它们排序后的最终位置。 正是这两种情况的结合才使希尔排序效率比 插入排序 高很多。Shell算法的性能与所选取的分组长度序列有很大关系。只对特定的待排序记录序列,可以准确地估算关键词的比较次数和对象移动次数。想要弄清关键词比较次数和记录移动次数与增量选择之间的关系,并给出完整的数学分析,至今仍然是数学难题。

/*
* 希尔插入排序法:最好O(n),最坏O(n^2)
*/
#ifndef __SHELL_SORT_H__
#define __SHELL_SORT_H__

/* 推理过程
 * 6 5 4 3 2 1		n=6		
 * 3	 6
 *   2     5
 *     1     4
 * 3 2 1 6 5 4		n/2=3
 * 2 3
 *   1 3
 *     3 6
 *	 5 6
 *	   4 6
 * 2 1 3 5 4 6		n/2=1
 * 1 2
 *   2 3
 *     3 5
 *	 4 5
 *	   5 6
 * 1 2 3 4 5 6		n/2=0
 */
template <class ElemType>
void ShellSort(ElemType elem[], int n) {
	for (int k = n / 2; k > 0; k /= 2){		// 取中间数n/2
		for (int i = k; i < n; i++) {		// i=n/2 且i++
			ElemType e = elem[i];
			int j = i;
			for (; j >= k && e < elem[j - k]; j -= k) {		// elem[i]和elem[i-k]比较
				elem[j] = elem[j - k];
			}
			elem[j] = e;
		}
	}
}

#endif // !__SHELL_SORT_H__

直接插入排序:

每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。 第一趟比较前两个数,然后把第二个数按大小插入到有序表中; 第二趟把第三个数据与前两个数从前向后扫描,把第三个数按大小插入到有序表中;依次进行下去,进行了(n-1)趟扫描以后就完成了整个排序过程。属于稳定的排序。

/*
* 直接插入排序法:最好O(n),最坏O(n^2)
*/
#ifndef __STRAIGHT_INSERT_SORT_H__
#define __STRAIGHT_INSERT_SORT_H__

/*
* 有n个数,循环n-1次,从第二个数a[i](i=1)开始,和它前面一个数比,如果小于前面的数则交换两个数,不小于退出进入下个循环,
* 下个循环从a[i+1]开始,和它前面一个数a[i]比,如果小于前面的数交换并且继续和a[i](i=i-1)比,直到不小于退出进入下个循环.
*/
template <class ElemType>
void StraightInsertSort(ElemType elem[], int n) {
	for (int i = 1; i < n; i++) {
		ElemType e = elem[i];
		int j;
		for (j = i - 1; j >= 0 && e < elem[j]; j--) {
			elem[j + 1] = elem[j];		// e前面的数后移
		}
		elem[j + 1] = e;	//j+1=i即elem[j + 1]=elem[i]
	}
}

#endif // !__STRAIGHT_INSERT_SORT_H__


节点和链表类,为用链表排序做准备。

node.h:

<span style="font-size:18px;">#ifndef __NODE_H__
#define __NODE_H__

// 结点类
template <class ElemType>
struct Node 
{
// 数据成员:
	ElemType data;				// 数据域
	Node<ElemType> *next;		// 指针域

// 构造函数:
	Node();						// 无参数的构造函数
	Node(ElemType item, Node<ElemType> *link = NULL);	// 已知数数据元素值和指针建立结构
};

// 结点类的实现部分
template<class ElemType>
Node<ElemType>::Node()
// 操作结果:构造指针域为空的结点
{
   next = NULL;
}

template<class ElemType>
Node<ElemType>::Node(ElemType item, Node<ElemType> *link)
// 操作结果:构造一个数据域为item和指针域为link的结点
{
   data = item;
   next = link;
}

#endif</span>

lk_list.h:
<span style="font-size:18px;">#ifndef __LK_LIST_H__
#define __LK_LIST_H__

#include "node.h"

template <class ElemType>
class LinkList
{
protected:
	Node<ElemType> *head;// 头结点指针
	mutable int cpos;			// 当前位置的序号
	mutable Node<ElemType> * curPtr;	// 指向当前位置的指针
	int count;							// 元素个数

	Node<ElemType> *GetElemPtr(int position) const;	// 返回指向第position个结点的指针
	void Init();						// 初始化线性表

public:
	LinkList();
	virtual ~LinkList();
	int Length() const;				    // 求线性表长度
	bool Empty() const;					// 判断线性表是否为空
	void Clear();						// 将线性表清空
	void Traverse() const;	// 遍历线性表
	int GetCurPosition() const;								// 返回当前位置
	bool GetElem(int position, ElemType &e) const;	// 求指定位置的元素	
	bool SetElem(int position, const ElemType &e);	// 设置指定位置的元素值
	bool Delete(int position, ElemType &e);			// 删除元素		
	bool Insert(int position, const ElemType &e);	// 插入元素
	LinkList(const LinkList<ElemType> &copy); // 复制构造函数模板
	LinkList<ElemType> &operator =(const LinkList<ElemType> &copy); // 重载赋值运算符
};

template <class ElemType>
Node<ElemType> *LinkList<ElemType>::GetElemPtr(int position) const {
	if (cpos > position) {
		cpos = 0;
		curPtr = head;
	}

	for (; cpos < position; cpos++) {
		curPtr = curPtr->next;
	}

	return curPtr;
}

template <class ElemType>
void LinkList<ElemType>::Init() {
	head = new Node<ElemType>;
	curPtr = head;
	cpos = 0;
	count = 0;
}

template <class ElemType>
LinkList<ElemType>::LinkList()
{
	Init();
}

template <class ElemType>
LinkList<ElemType>::~LinkList()
{
	delete head;
}

template <class ElemType>
int LinkList<ElemType>::Length() const
// 操作结果:返回线性表元素个数
{
	return count;
}

template <class ElemType>
bool LinkList<ElemType>::Empty() const {
	return head->next == NULL;
}

template <class ElemType>
void LinkList<ElemType>::Clear() {
	ElemType e;
	while (Length() > 0)
	{
		Delete(1, e);
	}
}

template <class ElemType>
void LinkList<ElemType>::Traverse() const {
	for (Node<ElemType> *n = head->next; n != NULL; n = n->next) {
		cout << n->data << " ";
	}
	cout << endl;
}

template <class ElemType>
int LinkList<ElemType>::GetCurPosition() const {
	return cpos;
}

template <class ElemType>
bool LinkList<ElemType>::GetElem(int position, ElemType &e) const {
	if (position < 1 || position > Length())
	{	// position范围错
		return false;				// 元素不存在
	}
	else {
		Node<ElemType> *n;
		n = GetElemPtr(position);
		e = n->data;
		return true;
	}
}

template <class ElemType>
bool LinkList<ElemType>::SetElem(int position, const ElemType &e) {
	if (position < 1 || position > Length())
	{	// position范围错
		return false;				// 元素不存在
	}
	else {
		Node<ElemType> *n;
		n = GetElemPtr(position);
		n->data = e;
		return true;
	}
}

template <class ElemType>
bool LinkList<ElemType>::Delete(int position, ElemType &e) {
	if (position < 1 || position > Length())
	{	// position范围错
		return false;				// 元素不存在
	}
	else {
		Node<ElemType> *n;
		n = GetElemPtr(position - 1);
		Node<ElemType> *nn = n->next;		// nn为n的下一个后继节点
		n->next = nn->next;
		e = nn->data;
		if (position == Length()) {
			cpos = 0;
			curPtr = head;
		}
		else {
			cpos = position;
			curPtr = n->next;
		}
		count--;
		delete nn;
		return true;
	}
}

template <class ElemType>
bool LinkList<ElemType>::Insert(int position, const ElemType &e) {
	if (position < 1 || position > Length() + 1)
	{	// position范围错
		return false;				// 元素不存在
	}
	else {
		Node<ElemType> *n;
		n = GetElemPtr(position - 1);
		Node<ElemType> *newn;
		newn = new Node<ElemType>(e, n->next);
		n->next = newn;
		cpos = position;
		curPtr = newn;
		count++;
		return true;
	}
}

template <class ElemType>
LinkList<ElemType>::LinkList(const LinkList<ElemType> &copy) {
	int copylen = copy.Length();
	ElemType e;
	Init();

	for (int cpos = 1; cpos <= copylen; cpos++) {
		copy.GetElem(cpos, e);
		Insert(Length() + 1, e);
	}
	cout << "调用拷贝构造函数" << endl;
}

template <class ElemType>
LinkList<ElemType> &LinkList<ElemType>::operator = (const LinkList<ElemType> &copy) {
	if (&copy != this) {
		int copylen = copy.Length();
		ElemType e;
		Clear();

		for (int cpos = 1; cpos <= copylen; cpos++) {
			copy.GetElem(cpos, e);
			Insert(Length() + 1, e);
		}
	}
	cout << "调用赋值函数" << endl;
	return *this;
}

#endif // !__LK_LIST_H__</span>


桶排序:

将需要排序的数放到有限个桶,然后按照桶排好的顺序收集数据,如若桶中的数据 > 1,则用其他优秀的算法进行排序(如快速排序等)。如果桶的数量恰好等于数的数量,则效率最好,为O(n),当然我们不可能这么做,因为对于数量庞大排序,消耗空间太大。假设n个数,有m个桶,则平均复杂度:O(n)+O( m*(n/m)*log(n/m) ) = O( n+n*(log n - log m) ),排序期间相等的数不会改变顺序,依次属于稳定排序。


<pre name="code" class="cpp">/*
* 桶排序法:平均:O(N+N*logN-N*logM),最好:O(N)
*/
#ifndef __BUCKET_SORT_H__
#define __BUCKET_SORT_H__

#include "lk_list.h"

// n为数的个数,m为桶的个数
template <class ElemType> 
void BucketSort(ElemType elem[], int n, int m)
{
	LinkList<ElemType> *list;	// 用于存储被分配的线性表数组
	list = new LinkList<ElemType>[10];
	int num = Distribute(elem, n, m, list);	// 分配
	SortList(list, n, num);		// 对分配好的list逐一进行排序,这里可以选择先进的排序方法提高效率
	Colect(elem, n, list);		// 收集
	delete[]list;
}

template <class ElemType>
int Distribute(ElemType elem[], int n, int m, LinkList<ElemType> list[])
{
	int num = 0;
	for (int i = 0; i < n; i++)
	{	// 进行第i起分配
		int index = elem[i] / m;
		// 将list视为桶,符合条件的数放入相应的桶中
		list[index].Insert(list[index].Length() + 1, elem[i]);
		if (num < index)	num = index;
	}
	// 返回桶个数,进而对各个桶内的数据进行排序
	return num;
}

template <class ElemType>
void SortList(LinkList<ElemType> *list, int n, int num)
{
	for (int i = 0; i < num; i++)
	{
		// 对各个桶内的数据进行排序,这里可替换为高效的排序算法
		QuickSort(list[i], n);
	}
}

template <class ElemType>
void Colect(ElemType elem[], int n, LinkList<ElemType> list[])
{
	for (int k = 0, j = 0; j < 10; j++)
	{	// 进行第i起分配
		ElemType tmpElem;
		// 逐个桶按顺序收集数据
		while (!list[j].Empty())
		{	// 收集list[j]
			list[j].Delete(1, tmpElem);
			elem[k++] = tmpElem;
		}
	}
}

#endif // !__BUCKET_SORT_H__
 


基数排序:

排序时,假设我们排序的数的范围在0~999之间,则关键码digits分为个位、十位、百位,共3个,基数r(关键字的取值范围)为0~9(10位)。

假设有n=7个数,为 

33,2,65,312,98,9,120

根据基数分出10个桶,依照个位分别放入桶内

0 1 2 3 4 5 6 7 8 9

120 2 33 65 98 9

312

排序后:           120,   2,    312,   33,   65,    98,    9

接着按照十位:2, 9, 312, 120, 33, 65, 98

百位:               2, 9, 33, 65, 98, 120, 312

时间复杂度:O( d*(r+n) ),其中d是关键码位数,r为基数,n为数的个数。且基数排序为稳定的排序。

radix_sort.h:

/*
 * 基数排序法:O(n^2)
 */
#ifndef __RADIX_SORT_H__
#define __RADIX_SORT_H__

#include "lk_list.h"

template <class ElemType>
void RadixSort(ElemType elem[], int n, int r)
{
	int digits;		// d为关键字位数
	digits = NumOfDigits(elem, n);		// 判断数组中的最高位并返回位数
	LinkList<ElemType> *list;	// 用于存储被分配的线性表数组
	list = new LinkList<ElemType>[r];
	for (int i = 1; i <= digits; i++)
	{	// 第i趟分配与收集
		Distribute(elem, n, r, i, list);	// 分配
		Colect(elem, n, r, list);		// 收集
	}
	delete []list;
}


template <class ElemType>
void Distribute(ElemType elem[], int n, int r, int i, LinkList<ElemType> list[])
// 初始条件: r为基数,list[0 .. r - 1]为被分配的线性表数组
// 操作结果: 进行第i趟分配
{
	for (int power = (int)pow((double)r, i - 1), j = 0; j < n; j++)
	{	// 进行第i起分配
		int index = (elem[j] / power) % r;
		list[index].Insert(list[index].Length() + 1, elem[j]);
	}
}

template <class ElemType>
void Colect(ElemType elem[], int n, int r, LinkList<ElemType> list[])
// 初始条件: r为基数,list[0 .. r - 1]为被分配的线性表数组
// 操作结果: 进行第i趟收集
{
	for (int k = 0, j = 0; j < r; j++)
	{	// 进行第i起分配
		ElemType tmpElem;
		while (!list[j].Empty())
		{	// 收集list[j]
			list[j].Delete(1, tmpElem);
			elem[k++] = tmpElem;
		}
	}
}

template <class ElemType>
int NumOfDigits(ElemType elem[], int n)
{
	int largest = 0;
	for (int i = 0; i < n; i++) {
		if (elem[i] > largest)
			largest = elem[i];
	}
	int digits = 0;//digits为最大值的位数
	while (largest)
	{
		digits++;
		largest /= 10;
	}
	return digits;
}

#endif

源代码:http://download.csdn.net/detail/u013707014/9032507


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值