10种排序算法总结(冒泡、选择、插入、希尔、归并、快速、堆、拓扑、锦标赛、基数)
2011年01月20日 星期四 08:24 P.M.
(1)执行时间
(2)存储空间
(3)编程工作
主要排序法有:
一、冒泡(Bubble)排序——相邻交换
二、选择排序——每次最小/大排在相应的位置
三、插入排序——将下一个插入已排好的序列中
四、壳(Shell)排序——缩小增量
五、归并排序
六、快速排序
七、堆排序
八、拓扑排序
九、锦标赛排序
十、基数排序
一、冒泡(Bubble)排序
----------------------------------Code 从小到大排序n个数------------------------------------
void BubbleSortArray()
{
}
-------------------------------------------------Code------------------------------------------------
效率 O(n²),适用于排序小列表。
二、选择排序
----------------------------------Code 从小到大排序n个数--------------------------------
void SelectSortArray()
{
}
}
}
-------------------------------------------------Code-----------------------------------------
效率O(n²),适用于排序小的列表。
三、插入排序
--------------------------------------------Code 从小到大排序n个数-------------------------------------
void InsertSortArray()
{
for(int i=1;i<n;i++)//循环从第二个数组元素开始,因为arr[0]作为最初已排序部分
{
while (j>=0 && arr[j]>temp)
{
}
arr[j+1]=temp;
}
}
------------------------------Code--------------------------------------------------------------
最佳效率O(n);最糟效率O(n²)与冒泡、选择相同,适用于排序小列表
若列表基本有序,则插入排序比冒泡、选择更有效率。
四、壳(Shell)排序——缩小增量排序
-------------------------------------Code 从小到大排序n个数-------------------------------------
void ShellSortArray()
{
{
{
}
arr[j+incr]=temp;
}
}
}
}
--------------------------------------Code-------------------------------------------
适用于排序小列表。
效率估计O(nlog2^n)~O(n^1.5),取决于增量值的最初大小。建议使用质数作为增量值,因为如果增量值是2的幂,则在下一个通道中会再次比较相同的元素。
壳(Shell)排序改进了插入排序,减少了比较的次数。是不稳定的排序,因为排序过程中元素可能会前后跳跃。
五、归并排序
----------------------------------------------Code 从小到大排序---------------------------------------
void MergeSort(int low,int high)
{
{
}
else
}
for(
}
-----------------------------------------------------Code---------------------------------------------------
效率O(nlogn),归并的最佳、平均和最糟用例效率之间没有差异。
适用于排序大列表,基于分治法。
六、快速排序
------------------------------------Code--------------------------------------------
----------------------------------------Code-------------------------------------
平均效率O(nlogn),适用于排序大列表。
此算法的总时间取决于枢纽值的位置;选择第一个元素作为枢纽,可能导致O(n²)的最糟用例效率。若数基本有序,效率反而最差。选项中间值作为枢纽,效率是O(nlogn)。
基于分治法。
七、堆排序
最大堆:后者任一非终端节点的关键字均大于或等于它的左、右孩子的关键字,此时位于堆顶的节点的关键字是整个序列中最大的。
思想:
(1)令i=l,并令temp= kl ;
(2)计算i的左孩子j=2i+1;
(3)若j<=n-1,则转(4),否则转(6);
(4)比较kj和kj+1,若kj+1>kj,则令j=j+1,否则j不变;
(5)比较temp和kj,若kj>temp,则令ki等于kj,并令i=j,j=2i+1,并转(3),否则转(6)
(6)令ki等于temp,结束。
-----------------------------------------Code---------------------------
void HeapSort(SeqIAst R)
堆排序的时间,主要由建立初始堆和反复重建堆这两部分的时间开销构成,它们均是通过调用Heapify实现的。
堆排序与直接插入排序的区别:
八、拓扑排序
例 :学生选修课排课先后顺序
拓扑排序:把有向图中各顶点按照它们相互之间的优先关系排列成一个线性序列的过程。
方法:
在有向图中选一个没有前驱的顶点且输出
从图中删除该顶点和所有以它为尾的弧
重复上述两步,直至全部顶点均已输出(拓扑排序成功),或者当图中不存在无前驱的顶点(图中有回路)为止。
---------------------------------------Code--------------------------------------
void TopologicalSort()
{
}
----------------------------------------Code--------------------------------------
算法的时间复杂度O(n+e)。
九、锦标赛排序
锦标赛排序的算法思想与体育比赛类似。
--------------------------------Code in C---------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define SIZE 100000
#define MAX 1000000
struct node
{
}data[SIZE];
long CompareNum=0;
long ExchangeNum=0;
long Read(char name[])//读取文件a.txt中的数据,并存放在数组data[]中;最后返回数据的个数
{
}
long Create(long num)//创建胜者树,返回冠军(最小数)在数组data[]中的下标
{
}
void TournamentSort(long num)//锦标赛排序
{
}
int main()
{
}
------------------------------------------Code-------------------------------------
十、基数排序
基数排序又被称为桶排序。与前面介绍的几种排序方法相比较,基数排序和它们有明显的不同。
通过使用对多关键字进行排序的这种“分配”和“收集”的方法,基数排序实现了对多关键字进行排序。
———————————————————————————————————————
例:
其二:可以先按面值排序分成十三堆(每一堆牌具有相同的面值),然后将这十三堆牌按面值从小到大的顺序叠放在一起,再把整副牌按顺序根据花色再分成四堆(每一堆牌已按面值从小到大的顺序有序),最后将这四堆牌按花色从小到大合在一起就得到排序的结果。
———————————————————————————————————————
实现方法:
最高位优先(Most Significant Digit first)法,简称MSD法:先按k1排序分组,同一组中记录,关键码k1相等,再对各组按k2排序分成子组,之后,对后面的关键码继续这样的排序分 组,直到按最次位关键码kd对各子组排序后。再将各组连接起来,便得到一个有序序列。
最低位优先(Least Significant Digit first)法,简称LSD法:先从kd开始排序,再对kd-1进行排序,依次重复,直到对k1排序后便得到一个有序序列。
---------------------------------Code in C#------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LearnSort
{
class Program
{
static void Main(string[] args)
{
int[] arr = CreateRandomArray(10);//产生随机数组
Print(arr);//输出数组
RadixSort(ref arr);//排序
Print(arr);//输出排序后的结果
Console.ReadKey();
}
public static void RadixSort(ref int[] arr)
{
int iMaxLength = GetMaxLength(arr);
RadixSort(ref arr, iMaxLength);
}
private static void RadixSort(ref int[] arr, int iMaxLength)
{
List<int> list = new List<int>();//存放每次排序后的元素
List<int>[] listArr = new List<int>[10];//十个桶
char currnetChar;//存放当前的字符比如说某个元素123 中的2
string currentItem;//存放当前的元素比如说某个元素123
for (int i = 0; i < listArr.Length; i++)//给十个桶分配内存初始化。
listArr[i] = new List<int>();
for (int i = 0; i < iMaxLength; i++)//一共执行iMaxLength次,iMaxLength是元素的最大位数。
{
foreach (int number in arr)//分桶
{
currentItem = number.ToString();//将当前元素转化成字符串
try { currnetChar = currentItem[currentItem.Length-i-1]; }//从个位向高位开始分桶
catch { listArr[0].Add(number); continue; }//如果发生异常,则将该数压入listArr[0]。比如说5 是没有十位数的,执行上面的操作肯定会发生越界异常的,这正是期望的行为,我们认为5的十位数是0,所以将它压入listArr[0]的桶里。
switch (currnetChar)//通过currnetChar的值,确定它压人哪个桶中。
{
case '0': listArr[0].Add(number); break;
case '1': listArr[1].Add(number); break;
case '2': listArr[2].Add(number); break;
case '3': listArr[3].Add(number); break;
case '4': listArr[4].Add(number); break;
case '5': listArr[5].Add(number); break;
case '6': listArr[6].Add(number); break;
case '7': listArr[7].Add(number); break;
case '8': listArr[8].Add(number); break;
case '9': listArr[9].Add(number); break;
default: throw new Exception("unknow error");
}
}
for (int j = 0; j < listArr.Length; j++)//将十个桶里的数据重新排列,压入list
foreach (int number in listArr[j].ToArray<int>())
{
list.Add(number);
listArr[j].Clear();//清空每个桶
}
arr = list.ToArray<int>();//arr指向重新排列的元素
//Console.Write("{0} times:",i);
Print(arr);//输出一次排列的结果
list.Clear();//清空list
}
}
//得到最大元素的位数
private static int GetMaxLength(int[] arr)
{
int iMaxNumber = Int32.MinValue;
foreach (int i in arr)//遍历得到最大值
{
if (i > iMaxNumber)
iMaxNumber = i;
}
return iMaxNumber.ToString().Length;//这样获得最大元素的位数是不是有点投机取巧了...
}
//输出数组元素
public static void Print(int[] arr)
{
foreach (int i in arr)
System.Console.Write(i.ToString()+'\t');
System.Console.WriteLine();
}
//产生随机数组。随机数的范围是0到1000。参数iLength指产生多少个随机数
public static int[] CreateRandomArray(int iLength)
{
int[] arr = new int[iLength];
Random random = new Random();
for (int i = 0; i < iLength; i++)
arr[i] = random.Next(0,1001);
return arr;
}
}
}
---------------------------------Code ---------------------------------------------
基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的比较性排序法。