![](http://image60.360doc.com/DownloadImg/2013/04/2218/31822564_1.jpg)
快排是目前公认的最好的排序算法,当排序关键字是随机分布是,平均时间最短。
这个关于排序的网站不错,还很全!!
http://www.cnblogs.com/kkun/archive/2011/11/23/2260312.html
一、选择排序
1. 基本思想:
每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
2. 排序过程:
【示例】:
初始关键字 [49 38 65 97 76 13 27 49]
第一趟排序后 13 [38 65 97 76 49 27 49]
第二趟排序后 13 27 [65 97 76 49 38 49]
第三趟排序后 13 27 38 [97 76 49 65 49]
第四趟排序后 13 27 38 49 [49 97 65 76]
第五趟排序后 13 27 38 49 49 [97 97 76]
第六趟排序后 13 27 38 49 49 76 [76 97]
第七趟排序后 13 27 38 49 49 76 76 [ 97]
最后排序结果 13 27 38 49 49 76 76 97
3.
void selectionSort(Type* arr,long len){
long i=0,j=0;/*iterator value*/
long maxPos;
assertF(arr!=NULL,"In InsertSort sort,arr is NULL\n");
for(i=len-1;i>=1;i--) {
maxPos=i;
for(j=0;j<i;j++)
if(arr[maxPos]<arr[j])maxPos=j;
if(maxPos!=i)swapArrData(arr,maxPos,i);
}
}
![视觉直观感受7种常用排序算法](http://cdn2.jobbole.com/2012/01/Visual-and-intuitive-feel-of-7-common-sorting-algorithms4.gif)
选择排序法的第一层循环从起始元素开始选到倒数第二个元素,主要是在每次进入的第二层循环之 前,将外层循环的下标赋值给临时变量,接下来的第二层循环中,如果发现有比这个最小位置处的元素更小的元素,则将那个更小的元素的下标赋给临时变量,最 后,在二层循环退出后,如果临时变量改变,则说明,有比当前外层循环位置更小的元素,需要将这两个元素交换.
二.直接插入排序
/// <summary>
/// 插入排序
/// </summary>
/// <param name="unsorted"></param>
static void insertion_sort(int[] unsorted)
{
for (int i = 1; i < unsorted.Length; i++)
{
if (unsorted[i - 1] > unsorted[i])
{
int temp = unsorted[i];
int j = i;
while (j > 0 && unsorted[j - 1] > temp)
{
unsorted[j] = unsorted[j - 1];
j--;
}
unsorted[j] = temp;
}
}
}
static void Main(string[] args)
{
int[] x = { 6, 2, 4, 1, 5, 9 };
insertion_sort(x);
foreach (var item in x)
{
if (item > 0)
Console.WriteLine(item + ",");
}
Console.ReadLine();
}
三. 冒泡排序
[算法思想]:将被排序的记录数组R[1..n]垂直排列,每个记录R[i]看作是重量为 R[i].key的气泡。根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R:凡扫描到违反本原则的轻气泡,就使其向上"飘浮"。如此反复进行,直到 最后任何两个气泡都是轻者在上,重者在下为止。
[算法]:
void BubbleSort(SeqList R) {
//R(l..n)是待排序的文件,采用自下向上扫描,对R做冒泡排序
int i,j;
Boolean exchange; //交换标志
for(i=1;i<n;i++){ //最多做n-1趟排序
exchange=FALSE; //本趟排序开始前,交换标志应为假
for(j=n-1;j>=i;j--) //对当前无序区R[i..n]自下向上扫描
if(R[j+1].key<R[j].key){//交换记录
R[0]=R[j+1]; //R[0]不是哨兵,仅做暂存单元
R[j+1]=R[j];
R[j]=R[0];
exchange=TRUE; //发生了交换,故将交换标志置为真
}
if(!exchange) return;//本趟排序未发生交换,提前终止算法
} //endfor(外循环)
} //BubbleSort
[分析]:起泡排序的结束条件为:最后一趟没有进行“交换”。从起泡排序的过程可 见,起泡排序是一个增加有序序列长度的过程,也是一个缩小无序序列长度的过程,每经过一趟起泡,无序序列的长度只缩小1。 [算法思想]:将被排序的记录数组R[1..n]垂直排列,每个记录R[i]看作是重量为R[i].key的气泡。根据轻气泡不能在重气泡之下的原则,从 下往上扫描数组R:凡扫描到违反本原则的轻气泡,就使其向上"飘浮"。如此反复进行,直到最后任何两个气泡都是轻者在上,重者在下为止。
![](http://cdn2.jobbole.com/2012/01/Visual-and-intuitive-feel-of-7-common-sorting-algorithms5.gif)
四. 希尔排序
基本思想:
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插人排序;然 后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记 录放在同一组中进行直接插入排序为止。
该方法实质上是一种分组插入方法。
给定实例的shell排序的排序过程
假设待排序文件有10个记录,其关键字分别是:
49,38,65,97,76,13,27,49,55,04。
增量序列的取值依次为:
5,3,1
依次实现间隔内排序
static void shell_sort(int[] unsorted, int len)
{
int group, i, j, temp;
for (group = len / 2; group > 0; group /= 2)
{
for (i = group; i < len; i++)
{
for (j = i - group; j >= 0; j -= group)
{
if (unsorted[j] > unsorted[j + group])
{
temp = unsorted[j];
unsorted[j] = unsorted[j + group];
unsorted[j + group] = temp;
}
}
}
}
}
static void Main(string[] args)
{
int[] x = { 6, 2, 4, 1, 5, 9 };
shell_sort(x, x.Length);
foreach (var item in x)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
![视觉直观感受7种常用排序算法](http://cdn2.jobbole.com/2012/01/Visual-and-intuitive-feel-of-7-common-sorting-algorithms6.gif)
五. 堆排序
由于它在直接选择排序的基础上利用了比较结果形成。效率提高很大。它完成排序的总比较次数为O(nlog2n)。它是对数据的有序性不敏感的一种算法。但堆排序将需要做两个步骤:-是建堆,二是排序(调整堆)。所以一般在小规模的序列中不合适,但对于较大的序列,将表现出优越的性能。
![Heap4](https://i-blog.csdnimg.cn/blog_migrate/fe4640418c11f9a879018b39d8832e82.png)
![视觉直观感受7种常用排序算法](http://cdn2.jobbole.com/2012/01/Visual-and-intuitive-feel-of-7-common-sorting-algorithms3.gif)
堆排序的C语言实现
04 | void HeapSort( int num[], int size); |
05 | void BuildHeap( int num[] , int size); |
06 | void PercolateDown( int num[] , int index, int size); |
07 | void PrintHeap( const char * strMsg, int array[], int nLength); |
08 | void Swap( int num[] , int v, int u); |
10 | int main( int argc, char *argv[]) |
12 | int data[13]={8,5,4,6,13,7,1,9,12,11,3,10,2}; |
20 | void HeapSort( int num[] , int size) |
25 | PrintHeap( "Befor Sort:" ,num,iLength); |
29 | for (i = iLength - 1; i >= 1; i--) { |
32 | PercolateDown(num, 0,size); |
33 | PrintHeap( "Sort Heap:" ,num,iLength); |
38 | void BuildHeap( int num[] , int size) { |
40 | for (i = size / 2 - 1; i >= 0; i--) { |
41 | PercolateDown(num, i,size); |
42 | PrintHeap( "Build heap:" ,num,size); |
47 | void PercolateDown( int num[] , int index, int size) { |
49 | while (index * 2 + 1<size) { |
51 | if (index * 2 + 2<size) { |
52 | if (num[min] > num[index * 2 + 2]) { |
57 | if (num[index] < num[min]) { |
60 | Swap(num, index, min); |
67 | void Swap( int num[] , int v, int u) { |
73 | void PrintHeap( const char * strMsg, int array[], int nLength) |
77 | for (i=0;i<nLength;i++) |
79 | printf ( "%d " ,array[i]); |
下面也是C语言的实现,稍微改动了下:
04 | void HeapSort( int num[], int size); |
05 | void BuildHeap( int num[] , int size); |
06 | void PercolateDown( int num[] , int index, int size); |
07 | void PrintHeap( const char * strMsg, int array[], int nLength); |
08 | void Swap( int num[] , int v, int u); |
10 | int main( int argc, char *argv[]) |
13 | int data[13]={8,5,4,6,13,7,2,9,12,11,3,10,1}; |
21 | void HeapSort( int num[] , int size) |
26 | PrintHeap( "Befor Sort:" ,num,iLength); |
30 | for (i = iLength - 1; i >= 1; i--) { |
33 | PercolateDown(num, 0,size); |
34 | PrintHeap( "Sort Heap:" ,num,iLength); |
40 | void BuildHeap( int num[] , int size) { |
43 | for (i = size / 2 - 1; i >= 0; i--) { |
44 | PercolateDown(num, i,size); |
45 | PrintHeap( "Build heap:" ,num,size); |
52 | void PercolateDown( int num[] , int index, int size) { |
54 | while (index * 2 + 1<size) { |
56 | if (index * 2 + 2<size) { |
57 | if (num[min] > num[index * 2 + 2]) { |
62 | if (num[index] < num[min]) { |
65 | Swap(num, index, min); |
72 | void Swap( int num[] , int v, int u) { |
78 | void PrintHeap( const char * strMsg, int array[], int nLength) |
82 | for (i=0;i<nLength;i++) |
84 | printf ( "%d " ,array[i]); |
六. 快速排序
快速排序的基本思路是:首先我们选择一个中间值middle(程序中我们可使用数组中间值),把比中间值小的放在其左边,比中间值大的放在其右边。由于这个排序算法较复杂,我们先给出其进行一次排序的程序框架(从各类数据结构教材中可得)
![视觉直观感受7种常用排序算法](http://cdn2.jobbole.com/2012/01/Visual-and-intuitive-feel-of-7-common-sorting-algorithms.gif)
static int partition(int[] unsorted, int low, int high)
{
int pivot = unsorted[low];
while (low < high)
{
while (low < high && unsorted[high] > pivot) high--;
unsorted[low] = unsorted[high];
while (low < high && unsorted[low] <= pivot) low++;
unsorted[high] = unsorted[low];
}
unsorted[low] = pivot;
return low;
}
static void quick_sort(int[] unsorted, int low, int high)
{
int loc = 0;
if (low < high)
{
loc = partition(unsorted, low, high);
quick_sort(unsorted, low, loc - 1);
quick_sort(unsorted, loc + 1, high);
}
}
static void Main(string[] args)
{
int[] x = { 6, 2, 4, 1, 5, 9 };
quick_sort(x, 0, x.Length - 1);
foreach (var item in x)
{
Console.WriteLine(item + ",");
}
Console.ReadLine();
}
七,归并排序
原理:将原序列划分为有序的两个序列,然后利用归并算法进行合并,合并之后即为有序序列。
要点:归并、分治
static void merge(int[] unsorted, int first, int mid, int last, int[] sorted)
{
int i = first, j = mid;
int k = 0;
while (i < mid && j < last)
if (unsorted[i] < unsorted[j])
sorted[k++] = unsorted[i++];
else
sorted[k++] = unsorted[j++];
while (i < mid)
sorted[k++] = unsorted[i++];
while (j < last)
sorted[k++] = unsorted[j++];
for (int v = 0; v < k; v++)
unsorted[first + v] = sorted[v];
}
static void merge_sort(int[] unsorted, int first, int last, int[] sorted)
{
if (first + 1 < last)
{
int mid = (first + last) / 2;
Console.WriteLine("{0}-{1}-{2}", first, mid, last);
merge_sort(unsorted, first, mid, sorted);
merge_sort(unsorted, mid, last, sorted);
merge(unsorted, first, mid, last, sorted);
}
}
static void Main(string[] args)
{
int[] x = { 6, 2, 4, 1, 5, 9 };
int[] sorted = new int[x.Length];
merge_sort(x, 0, x.Length, sorted);
for (int i = 0; i < sorted.Length; i++)
{
if (x[i] > 0)
Console.WriteLine(x[i]);
}
Console.ReadLine();
}
八 基数排序Radix sort
原理类似桶排序,这里总是需要10个桶,多次使用
首先以个位数的值进行装桶,即个位数为1则放入1号桶,为9则放入9号桶,暂时忽视十位数
例如
待排序数组[62,14,59,88,16]简单点五个数字
分配10个桶,桶编号为0-9,以个位数数字为桶编号依次入桶,变成下边这样
| 0 | 0 | 62 | 0 | 14 | 0 | 16 | 0 | 88 | 59 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |桶编号
将桶里的数字顺序取出来,
输出结果:[62,14,16,88,59]
再次入桶,不过这次以十位数的数字为准,进入相应的桶,变成下边这样:
由于前边做了个位数的排序,所以当十位数相等时,个位数字是由小到大的顺序入桶的,就是说,入完桶还是有序
| 0 | 14,16 | 0 | 0 | 0 | 59 | 62 | 0 | 88 | 0 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |桶编号
因为没有大过100的数字,没有百位数,所以到这排序完毕,顺序取出即可
最后输出结果:[14,16,59,62,88]
代码仅供参考
/// <summary>
/// 基数排序
/// 约定:待排数字中没有0,如果某桶内数字为0则表示该桶未被使用,输出时跳过即可
/// </summary>
/// <param name="unsorted">待排数组</param>
/// <param name="array_x">桶数组第一维长度</param>
/// <param name="array_y">桶数组第二维长度</param>
static void radix_sort(int[] unsorted, int array_x = 10, int array_y = 100)
{
for (int i = 0; i < array_x/* 最大数字不超过999999999...(array_x个9) */; i++)
{
int[,] bucket = new int[array_x, array_y];
foreach (var item in unsorted)
{
int temp = (item / (int)Math.Pow(10, i)) % 10;
for (int l = 0; l < array_y; l++)
{
if (bucket[temp, l] == 0)
{
bucket[temp, l] = item;
break;
}
}
}
for (int o = 0, x = 0; x < array_x; x++)
{
for (int y = 0; y < array_y; y++)
{
if (bucket[x, y] == 0) continue;
unsorted[o++] = bucket[x, y];
}
}
}
}
static void Main(string[] args)
{
int[] x = { 999999999, 65, 24, 47, 13, 50, 92, 88, 66, 33, 22445, 10001, 624159, 624158, 624155501 };
radix_sort(x);
foreach (var item in x)
{
if (item > 0)
Console.WriteLine(item + ",");
}
Console.ReadLine();
}
几种算法的比较和选择
1.稳定性比较
插入排序、冒泡排序、二叉树排序、二路归并排序及其他线形排序是稳定的
选择排序、希尔排序、快速排序、堆排序是不稳定的
2.时间复杂性比较
| 平均情况 | 最好情况 | 最坏情况 |
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) |
基数排序 | O(n) | O(n) | O(n) |
快速排序 | O(nlogn) | O(nlogn) | O(n2) |
希尔排序 | O(n1.5) | O(n) | O(n1.5) |
插入排序 | O(n2) | O(n) | O(n2) |
选择排序 | O(n2) | O(n2) | O(n2) |
3.辅助空间的比较
线形排序、二路归并排序的辅助空间为O(n),其它排序的辅助空间为O(1);
4.其它比较
插入、冒泡排序的速度较慢,但参加排序的序列局部或整体有序时,这种排序能达到较快的速度。
反而在这种情况下,快速排序反而慢了。
当n较小时,对稳定性不作要求时宜用选择排序,对稳定性有要求时宜用插入或冒泡排序。
若待排序的记录的关键字在一个明显有限范围内时,且空间允许是用桶排序。
当n较大时,关键字元素比较随机,对稳定性没要求宜用快速排序。
当n较大时,关键字元素可能出现本身是有序的,对稳定性有要求时,空间允许的情况下。
宜用归并排序。
当n较大时,关键字元素可能出现本身是有序的,对稳定性没有要求时宜用堆排序。
相关知识介绍(所有定义只为帮助读者理解相关概念,并非严格定义):
1、稳定排序和非稳定排序
简单地说就是所有相等的数经过某种排序方法后,仍能保持它们在排序之前的相对次序,我们就
说这种排序方法是稳定的。反之,就是非稳定的。
比如:一组数排序前是a1,a2,a3,a4,a5,其中a2=a4,经过某种排序后为a1,a2,a4,a3,a5,
则我们说这种排序是稳定的,因为a2排序前在a4的前面,排序后它还是在a4的前面。假如变成a1,a4,
a2,a3,a5就不是稳定的了。
2、内排序和外排序
在排序过程中,所有需要排序的数都在内存,并在内存中调整它们的存储顺序,称为内排序;
在排序过程中,只有部分数被调入内存,并借助内存调整数在外存中的存放顺序排序方法称为外排序。
、 若n较小n〈=50采用直接插入或直接选择排序。
2、 若文件初始状态已基本有序正序直接插入冒泡或快速。
3、 N较大采用时间复杂度为Onlgn的算法快速堆或归并。 快速排序是目前认为最好的内部排序方法但是堆排序的辅助空间比快速排序少并且最坏情况比快速好但这两种排序算法都不稳定如要求稳定的排序算法用归并排序