常用排序算法的实现

1. 排序分类

In-place sort(不占用额外内存或占用常数的内存):插入排序、选择排序、冒泡排序、堆排序、快速排序。
Out-place sort:归并排序、计数排序、基数排序、桶排序。

内部排序:数据记录在内存中进行排序。
外部排序:排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。

stable sort:插入排序、冒泡排序、归并排序、计数排序、基数排序、桶排序。
unstable sort:选择排序(5 8 5 2 9)、快速排序、堆排序。


2. 对于不稳定的算法的改进

在每个输入元素加一个index,表示初始时的数组索引,当不稳定的算法排好序后,对于相同的元素对index排序即可。


3. 插入排序

将一个记录插入到已经有序的列表中,得到一个新的有序序列,序列长度增加1;
从序列的第一个数开始,看成有序的序列,然后从第二个数开始逐步插入,直到遍历全部序列中的数;
需要设立临时值;
对于相等元素的处理方法应该是相同的(要么放在有序序列中的值的前面,要么放在后面),所以插入排序稳定;

特点:stable sort、In-place sort
最优复杂度:当输入数组就是排好序的时候,复杂度为O(n),而快速排序在这种情况下会产生O(n^2)的复杂度。
最差复杂度:当输入数组为倒序时,复杂度为O(n^2)
插入排序比较适合用于“少量元素的数组”。


4. 希尔排序


先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。

希尔排序也是一种插入排序。


5. 选择排序

在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。

特性:In-place sort,unstable sort。
思想:每次找一个最小值。
最好情况时间:O(n^2)。
最坏情况时间:O(n^2)。


6. 堆排序

堆排序是一种树形选择排序,是对直接选择排序的有效改进。

特性:unstable sort、In-place sort。
最优时间:O(nlgn)
最差时间:O(nlgn)
思想:运用了最小堆、最大堆这个数据结构,而堆还能用于构建优先队列。


7. 冒泡排序


在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。

特点:stable sort、In-place sort
思想:通过两两交换,像水中的泡泡一样,小的先冒出来,大的后冒出来。
最坏运行时间:O(n^2)
最佳运行时间:O(n^2)

8. 快速排序

被誉为“20世纪十大经典算法之一”。

1)选择一个基准元素,通常选择第一个元素或者最后一个元素,
2)通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。
3)此时基准元素在其排好序后的正确位置
4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。

特性:unstable sort、In-place sort。
最坏运行时间:当输入数组已排序时,时间为O(n^2),当然可以通过随机化来改进(shuffle array 或者 randomized select pivot),使得期望运行时间为O(nlgn)。
最佳运行时间:O(nlgn)
快速排序的思想也是分治法。
当输入数组的所有元素都一样时,不管是快速排序还是随机化快速排序的复杂度都为O(n^2)


9. 归并排序

归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

特点:stable sort、Out-place sort
思想:运用分治法思想解决排序问题。
最坏情况运行时间:O(nlgn)
最佳运行时间:O(nlgn)


10. C/C++实现

/* 
*	八大排序算法简单实现
*	peic	2015/10/28
*
*   所有排序规则都是从小到大 
*/



#include <stdio.h>
#include <time.h>
#include <stdlib.h>

typedef int ElementType;

/*
*	打印数组
*/
void printList(ElementType* list, int len)
{
	//不同数据类型需要改变格式化输出
	int i;
	for(i=0; i < len-1; i++)
	{
		printf("%d, ", list[i]);
	}
	printf("%d\n", list[i]);
}


/*
*	产生随机数组
*/
void CreateRandList(ElementType* list, int size)
{
	int i;
	int t = 10, d = 100;

	//初始化随机化种子
	srand(time(NULL)); 

	for(i=0; i<size; i++)
	{
		//生成0-100随机数
		*(list + i) = rand()%(d - t) + t;
	}
	printf("Random List:\n");
	printList(list, size);
}


/*
*	两个数交换
*/
void swap(ElementType* a, ElementType* b)
{
	ElementType tmp = *a;
	*a = *b;
	*b = tmp;
}



/* 
*	直接插入排序,需要设置哨兵
*	稳定(对相等元素的处理是固定的)
*	复杂度n^2
*/
void InsertSort(ElementType* list, int len)
{
	int i, j;
	for(i = 1; i < len; i++)
	{
		//将一个数list[i],插入到有序数组中
		if(list[i] < list[i-1])
		{
			//将list[i]保存
			int tmp = list[i];
			
			//将前面排好序的数组后移,找到list[i]的位置
			j = i - 1;
			while(tmp < list[j])
			{
				list[j+1] = list[j];
				j--;
			}
			list[j+1] = tmp;
		}
	}
	printf("InsertSort result: \n");
	printList(list, len);
}


/* 
*	希尔排序,是插入排序的一种,也称缩小增量排序
*	不稳定
*	时间复杂读:比o(n^2)好一些,依赖于步长的选择
*/
void ShellSort(ElementType* list, int len)
{
	//初始步长为len/2
	int dk = len / 2;

	//一直做步长减少的插入排序,直到步长为1
	while(dk >= 1)
	{
		
		//对子序列做插入排序,从dk开始,与前面的数进行比较
		int i = 0;
		for(i=dk; i < len; i++)
		{
			//插入排序的子序列间隔为步长dk
			if(list[i] < list[i-dk])
			{
				ElementType tmp = list[i];
				int j = i - dk;
				while(tmp < list[j] && j >= 0)
				{
					list[j+dk] = list[j];
					j -= dk;
				}
				list[j+dk] = tmp;
			}
			//printList(list, len);
		}

		//缩短步长
		//printf("dk = %d\n", dk);
		//printList(list, len);
		dk /= 2;
	}
	printf("ShellSort result: \n");
	printList(list, len);
}


/* 
*	直接选择排序
*	每次选择子序列中最小的数和序列相应位置的值进行交换
*	遇到相等元素的每次操作都是一样的,所以排序是稳定的
*	可以分情况,每次选择数组中的最大值和最小值进行交换,减少循环次数
*/
void SelectedSort(ElementType* list, int len)
{
	int i,j;
	int min_postion = 0;
	for(i=0; i<len; i++)
	{
		min_postion = i;
		for(j=i+1; j<len; j++)
		{
			if(list[j] < list[min_postion])
			{
				min_postion = j;
			}
		}
		//printf("min――position:----%d:\n", min_postion);
		swap(list+i, list+min_postion);
	}
	printf("SelectedSort result: \n");
	printList(list, len);
}


/* 
*	堆排序(树形选择排序)
*/

/*
*	调整堆,将输入的数组调整成有序的堆(二叉树,满足左右子节点小于父节点)
*/
void AdjustHeap(ElementType* list, int parent, int len)
{
	//左节点一定存在
	int lchild, minchild;
	lchild = parent * 2 + 1;

	//adjust tree
	while(lchild < len)
	{
		//寻找较大的子节点,右节点不一定存在
		if((lchild + 1) < (len - 1) && list[lchild + 1] > list[lchild])
			minchild = lchild + 1;
		else
			minchild = lchild;
			
		//交换父节点和较大的子节点,使得父节点大于左右子节点
		if(list[parent] < list[minchild])
		{
			swap(list+minchild, list+parent);
			//因为子节点和父节点发生交换,需要向下更新
			parent = minchild;
			lchild = parent * 2 + 1;
		}
		else
			break;
	}
}

void BuildHeap(ElementType* list, int len)
{
	int i, lchild, minchild;
	for(int parent = (len - 1) / 2; parent >= 0; parent--) 
	{	
		AdjustHeap(list, parent, len);
	}
	printf("Tree result: \n");
	printList(list, len);
}

void HeapSort(ElementType* list, int len)
{
	for(int i = len; i >= 1; i--)
	{
		AdjustHeap(list, 0, i);
		swap(list, list + i - 1);
	}
	printf("HeapSort result: \n");
	printList(list, len);
}


/*
*	基本冒泡排序
*/
void BubleSort(ElementType* list, int len)
{
	for(int i = 0; i < (len - 1); i++)
	{
		for(int j = 0; j < len - i - 1; j++)
		{
			if (list[j+1] < list[j])
			{
				swap(list+j, list+j+1);
			}
		}
	}
	printf("BubleSort result: \n");
	printList(list, len);
}


/*
*	改进冒泡排序:一次循环中进行正反两次双向比较,减少循环次数
*/
void BubleImproveSort(ElementType* list, int len)
{
	int low = 0, high = len - 1;
	while(low++ <= high--)
	{
		for(int i = 0; i < high; i++)
		{
			if(list[i] > list[i+1])
				swap(list+i, list+i+1);
		}
		for(int j = high; j > low; j--)
		{
			if(list[j] < list[j-1])
				swap(list+j, list+j-1);
		}
	}
	printf("BubleImproveSort result: \n");
	printList(list, len);
}


/*
*	快速排序
*	在不用开辟新内存空间的排序算法中,效果最好
*	不稳定
*/
void QuickSort(ElementType* list, int len)
{
	int low = 0, high = len;
	int i, j;
	//初始化key
	ElementType key = list[low];
	
	//递归终止条件,序列长度为1则无需排序,返回
	if(len <= 1)
		return;


	//将序列以key为分界线分成小于key和大于key两部分
	while(low < high)
	{
		//从后向前,找到第一个比key小的数
		while(--high> low)
		{
			if(list[high] <= key)
			{
				//当从后向前遍历的时候,key在list[low]的位置上
				swap(list+high, list+low);
				//交换key和相应数的位置后,需要更新key的位置
				key = list[high];
				break;
			}
		}
		
		//从前往后,找第一个比key大的数
		while(++low < high)
		{
			if(list[low] >= key)
			{
				//当从前向前遍历的时候,key在list[high]的位置上
				swap(list+low, list+high);
				//交换key和相应数的位置后,需要更新key的位置
				key = list[low];
				break;
			}
		}
	}
	//printList(list, len);
	
	//递归两个子序列
	QuickSort(list, low);
	QuickSort(list+low, len - low);

	printf("QuickSort result: \n");
	printList(list, len);
}


/*
*	归并排序
*	利用分治思想,建立在归并操作上,可以先定义归并操作,然后递归调用
*/

/*
*	归并操作,将一个数组的两部分,归并到辅助数组中
*	list[start, centre]和list[centre+1, end]是待归并数组的两部分
*/
void Merge(ElementType* list, int start, int centre, int end, ElementType* result)
{
	int i=start, j=centre+1, k=start;
	
	//开辟新数组存放归并的序列
	
	//比较归并两个子序列,数值小的优先,直到一个序列比较完毕
	while( i <= centre && j <= end) 
	{
		if(list[i] < list[j])
			result[k++] = list[i++];

		else
			result[k++] = list[j++];
	}
	
	//将未完全归并的序列接在新数组的后面
	while(i <= centre)
		result[k++] = list[i++];	
	while(j <= end)
		result[k++] = list[j++];
	
	/*查看结果
	printf("---start==%d,----centre==%d,----end==%d\n", start, centre, end);
	printf("list--------\n");
	printList(list, 10);
	printf("result********\n");
	printList(result, 10);
	*/
	
	//将结果保存在原序列中(也可以在归并循环中直接将开辟的tmp和list进行交换)
	for (i = start; i <= end; i++)
		list[i] = result[i];
	
}

/*
*	归并排序
*/
void MergeSort(ElementType* list, int len)
{
	ElementType* tmp = (ElementType*)malloc(sizeof(ElementType) * len);
	
	//初始归并的每个子序列长度
	int subLength = 1;
	while(subLength < len)
	{
		//归并子序列的起点、中间位置和结束位置
		int start = 0;
		int mergeLength = 2 * subLength;
		
		//对序列的若干子序列进行循环归并
		//对等长子序列进行归并
		while(start + mergeLength < len)
		{
			//初始归并的子序列(数组的前两个数)
			int centre = start + subLength-1;
			int end = start + mergeLength-1;			
			Merge(list, start, centre, end, tmp);
			start = start + mergeLength;
		}
		//对不等长子序列进行归并
		if(start + subLength < len)
		{
			int centre = start + subLength - 1;
			int end = len - 1;
			Merge(list, start, centre, end, tmp);
		}
		//改变归并长度
		subLength *= 2;
	}
	free(tmp);
	printf("MergeSort result: \n");
	printList(list, len);
}





void main()
{
	const int size = 10;
	ElementType list[size];
	CreateRandList(list, size);
/*	
	ElementType list1[size];
	CreateRandList(list1, size);
	InsertSort(list1, size);
	
	
	ElementType list2[size];
	CreateRandList(list2, size);
	ShellSort(list2, size);

	ElementType list3[size];
	CreateRandList(list3, size);
	SelectedSort(list3, size);

	BuildHeap(list, size);
	HeapSort(list, size);

	BubleSort(list, size);
	
	BubleImproveSort(list, size);

	QuickSort(list, size);
*/
	MergeSort(list, size);

	system("pause");
}




本文关于排序算法的基本原理和知识,部分参考了下面的博文:


http://blog.csdn.net/hguisu/article/details/7776068
http://blog.csdn.net/xiazdong/article/details/8462393
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值