关于排序算法的一点总结

关于排序算法的一点总结

1.1概述

生活中处处存在排序,考试成绩要排序,年龄大小要排序,图书馆的藏书也要排序。排序也是数据结构课程的一个重要组成部分。教材中的出现的排序有名次排序、选择排序冒泡排序等,下面我们一一进行介绍。参看1

1.2排序算法

1.2.1名次排序 rank sort

所谓排序,也就是给排列的内容一个“排名”,也就是“第几”的“几”。比如,第三个,也就是前面有两个,“小于等于第三名”的有3个。名次排序的原理就来源于此:

  • 求出每个元素的“排名”,即“队列中所有比它小的元素数目+在它左边出现的与它相同的元素数目”。
  • 把“位次”记录到另一个数组中
  • 按照“位次”数组重新排列原数组的元素

参考代码:

template <class T>
void Rank(T a[], int n, int r[])
{ //计算元素的排名
	for (int i = 0; i < n; i++) r[i] = 0; //初始化
//逐对比较所有的元素。任两个都做比较,次数为(n-1)n/2 
	for (int i = 1; i < n; i++)
		for ( int j = 0; j < i; j++)
			if (a [j] <= a[i])   r[i]++;
			     else r[j]++;
}
template <class T>
void Rearrange (T a[], int n, int r[])
{ //按序重排数组a中的元素,使用附加数组u,数组r即为“排名”数组
	T *u = new T[n+1];
	//在u中按排名排列
	for (int i = 0; i < n; i++)
		u[r[i]] = a[i];
	//把u中的元素移回到a中
	for (int i = 0; i < n; i++)
		a[i] = u[i] ;
	delete [ ]u;
}

算法性能分析:
在计算排名的Rank函数中,比较次数为 ( n − 1 ) n 2 \frac{(n-1)n}{2} 2(n1)n,Rearrange函数中移动次数为 2 n 2n 2n,该排序算法的时间复杂度为O( n 2 n^2 n2)。

1.2.2选择排序 selection sort

对于给定的n个元素的数组:

  • 从中选出最大的元素,将其置于最后a[n-1]。
  • 余下的n-1个元素中选出最大,置于a[n-2]。
  • 以此类推,直至剩余一个元素。

举例:
选择排序举例
参考代码:

template<class T>
int Max(T a[], int n)
{// 确定a [0 : n-1]中的最大元素
	int pos = 0;
	for (int i = 1; i < n; i++)
		if (a[pos] < a[i])
			pos = i;
	return pos;
}
template <class T>
void selectionSort (T a[], int n)
{ 
	for ( int size = n;  size>1; size- -) { //作比较并交换
		int j= Max(a, size);//size-1次比较
		Swap(a[j],a[size-1] ); //移动3次
	}
}

算法性能分析:
每次调用Max函数需要执行size-1次比较,总比较次数为 ( n − 1 ) n 2 \frac{(n-1)n}{2} 2(n1)n,移动次数为 3 ( n − 1 ) 3(n-1) 3(n1)。时间复杂度O( n 2 n^2 n2)。

1.2.3冒泡排序 bubble sort

冒泡排序是一种简单的排序算法。它重复地走访要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

  • 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
  • 对每一对相邻元素作同样的工作,第一对到结最后一对,这样在最后的元素应该会是最大的数;
  • 针对所有的元素重复以上的步骤,除了最后一个;
  • 重复步骤1~3,直到排序完成。

参考代码:

template <class T>
void bubbleSort (T a[], int n)
{ 
	for (int i =n; i>1; i--) {
		for (int j = 0; j < n-1; j++) {
			if (a[j] > a[j + 1])  
			   swap(a[j], a[j + 1]);
		}
	}
}

算法性能分析:
该算法时间复杂度是 O ( n 2 ) O(n^2) O(n2)

###1.2.4插入排序 insert sort
插入排序是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。在从后向前扫描过程中,反复把已排序元素逐步向后挪位,为最新元素提供插入空间。举个形象的例子:打扑克牌摸牌的时候按牌的大小整理牌。
步骤:

  • 从第一个元素开始,该元素可以认为已经被排序
  • 取出下一个元素,在已经排序的元素序列中从后向前扫描
  • 如果扫描到的以排序的元素大于新元素,将该元素移到下一位置
  • 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  • 将新元素插入到该位置后
  • 重复步骤2~5

参考代码:

template<class T>
void insertionSort(T a[], int n)
{	
    for (int i=1; i < n; i++) {
      //将a[i]插入a [0 : i-1]
	T t=a[i];
	int j;
	for (j=i-1; j >= 0 && t<a[j];  j--)
	    a[j+1]=a[j];//向后移位,空出位置来插入
	    a[j+1]=t;
	} 
}  

算法性能分析:
该算法时间复杂度是 O ( n 2 ) O(n^2) O(n2)

1.2.5箱子排序 bin sort

箱子排序是指利用链表这种数据结构所设计的一种排序算法。这种排序首先把元素相同的节点放在同一个箱子里,然后通过把箱子链接起来就可以创建一个有序的链表。
箱子排序举例
箱子的理解和实现:

  • 每个箱子都是一个由节点组成的线性表。
  • 箱子中的节点数目介于0到n之间。
  • 把每个箱子都描述成一个链表。
  • 在进行节点分配之前,所有的箱子都是空的。

步骤:

  • 从欲排序链表的首部开始,逐个删除每个节点
  • 把所删除的节点放入适当的箱子中(即相应的链表中)
  • 收集并链接每个箱子中的节点,产生一个排序的链表

参考代码:

void binSort(Chain<Node>& X,int range)
{//按分数排序
	intlen=X.Length();   	Node x;
	Chain<Node> *bin;
	bin=new Chain<Node>[range+1];
	//分配到每个箱子中
	for(int i=1; i<=len; i++){
		X.Delete(1,x); //从X摘取第一个节点
		bin[x.score].Insert(0,x); 
		//向bin的合适位置插入节点
	}
	//从箱子中收集各元素,
	for(int j=range; j>=0; j--)
		while(!bin[j].IsEmpty()){
			bin[j].Delete(1,x);
			X.Insert(0,x);//链成新链表
		}
	delete[]bin;
}

算法性能分析:
两个for循环中执行的每一次插入和删除操作所需要的时间均为 Θ Θ Θ(1),因此第一个for循环的复杂性为 Θ Θ Θ(n),其中n为输入链表的长度。第二个for循环的复杂性为 Θ ( n + r a n g e ) Θ(n+range) Θ(n+range)。因此函数binSort总的复杂性为 Θ ( n + r a n g e ) Θ(n+range) Θ(n+range)

1.2.6基数排序 radix sort

基数排序也是非比较的排序算法。它是一种稳定的排序算法。有两种方法:最高位优先法(MSD)和最低位优先法(LSD)。通常用于对数的排序选择的是最低位优先法,即先对最次位关键字进行排序,再对高一位的关键字进行排序,以此类推。

类似于桶排序,我们需要给待排序记录准备10个桶,因为一个数的任何一位上,其数字大小都位于0~9之间,因此采用10个桶,桶的编号分别为0,1,2,3,4…9,与待排序记录中每个数相应位的数值对应,基数排序也是因此而得名。

步骤:

  • 利用箱子排序的方法,根据最低位数字(即个位数字)对数字进行排序
  • 利用箱子排序的方法,对第一步的结果按照次低位数字(即十位数字)进行排序
  • 依此顺序依次进行,共(最大值的位数)次
    图解:
    基数排序

算法性能分析:

  • 对于一般的基数r,相应的分解式为:
    x x x% r r r; ( x (x (x % r 2 r^2 r2) / r /r /r; ( x (x (x % r 3 r^3 r3)/ r 2 r^2 r2; …
  • 当使用基数 r = n r=n r=n对n个介于0~ n c n^c nc-1范围内的整数进行分解时,每个数将可以分解出c个数字。
  • 因此,可以采用c次n个箱子的箱子排序。整个排序所需要的时间为 Θ ( c n ) = Θ ( n ) Θ(cn)=Θ(n) Θ(cn)=Θ(n)(因为c是一个常量)。

总结:

排序法时间复杂度稳定性
名次排序 O ( n 2 ) O(n^2) O(n2)稳定
冒泡排序 O ( n 2 ) O(n^2) O(n2)稳定
选择排序 O ( n 2 ) O(n^2) O(n2)不稳定
插入排序 O ( n 2 ) O(n^2) O(n2)稳定
箱子排序 Θ Θ Θ(n+range)稳定
基数排序 Θ ( n ) Θ(n) Θ(n)稳定

(如有不当,还望指正)


  1. 数据结构、算法与应用 C++语言描述 第二版,(美)萨尼(Sahni,S.)著 ↩︎

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值