第十章 实现排序算法
排序的目的是将一组“无序”的记录序列调整为“有序”的记录序列,使之按关键字递增(或递减)次序排列起来
排序分类:
按是否涉及到数据的内、外存交换分类
1) 内部排序:在排序的过程中,若整个文件都是放到内存中处理,排序时不涉及数据的内外存交换,则称之为内部排序
2) 外部排序:若排序过程中要进行数据的内、外存交换,则称之为外部排序
按策略划分内部排序方法
1) 插入排序,2)选择排序,3)交换排序,4)归并排序,5)分配排序
插入排序
思想:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的适当位置,直到全部记录被插入完成为止。
1. 直接插入排序:
1)基本思想:假设待排序的记录存放在数组R[0…n-1]中,初始时,R[0]自成一个有序区,无序区为R[1…n-1].从i起直至i=n-1为止,依次将R[i]插入当前的有序区R[0…i-1]中,生成含n个记录的有序区
直接插入排序的基本操作是将当前无序区的第1个记录R[i]插入到有序区R[0…i-1]中的适当位置,使R[0…i]变为新的有序区,因为这种方法每次使有序区增加一个记录,通常称为增量法
插入排序与打扑克牌时整理手上的牌非常类似。摸来的第一张牌不用整理,此后每次从桌上的牌(无序区)中摸最上面的1张并插入左手的牌(有序区)中正确的位置。为了找到这个正确的位置,须自左至右(或自右至左)将摸来的牌与左手中的牌逐一比较
2) 示意图如下
3) 直接插入排序的时间复杂度为O(n2)
直接插入排序的算法实现为:
///<summary>
/// 直接插入排序
/// </summary>
/// <paramname="arr"></param>
/// <returns></returns>
public static int[]DirectInsertSort(int [] arr)
{
int len=arr.Length;
for (int i = 1; i < len; i++)
{
for (int j = i-1; j >= 0;j--)
{
if (arr[j+1] < arr[j])//相邻的交换
{
int tmp = arr[j+1];
arr[j + 1] = arr[j];
arr[j] = tmp;
}
else
{
break;
}
}
}
return arr;
}
2. 希尔排序
希尔排序是一种插入排序
1)基本思想:对待排记录序列进行“宏观”调整,再进行微观调整。
所谓“宏观”调整,指的是“跳跃式”的插入排序。即将记录序列分成若干个子序列,每个子序列分别进行插入排序。关键是,这种子序列不是由相邻的记录构成的。假设将n个记录分成d个子序列,则这d个子序列分别为:
{R[1],R[1+d],R[1+2d],…,R[1+kd]}
{R[2],R[2+d],R[2+2d],…,R[2+kd]}
…
{R[d],R[2d],R[3d],…,R[(k+1)d]
2)示意图为:
3). 希尔排序的时间复杂度为O(n(log2n)2)
4). 代码实现:
///<summary>
/// 直接插入排序
/// </summary>
/// <paramname="arr"></param>
/// <returns></returns>
public static int[] shellSort(int[] arr)
{
int d = 3;//假设当前增量为3
while (d > 0)//排序的趟数
{
for (int i = 0; i < d;i++)//分成d段分别进行插入排序
{
for (int j = i; j<arr.Length; j+=d)//插入排序
{
for (int k = j-d; k>= 0; k -= d)//与之前的进行比较,找到合适的位置,并交换
{
if (arr[k + d]< arr[k])
{
int tmp =arr[k + d];
arr[k + d] = arr[k];
arr[k] = tmp;
}
else
{
break;
}
}
}
}
d--;
}
return arr;
}