关于排序算法的一点总结
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(n−1)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(n−1)n,移动次数为
3
(
n
−
1
)
3(n-1)
3(n−1)。时间复杂度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) | 稳定 |
(如有不当,还望指正)
数据结构、算法与应用 C++语言描述 第二版,(美)萨尼(Sahni,S.)著 ↩︎