基本概念
假设含有n个记录的序列为{r1,r2,...,rn},其相应的关键字分别为{k1,k2,...,kn},需确定1,2,3...,n的一种排列p1,p2,...,pn,使其相应的关键字满足kp1<=kp2<=...<=kpn非递减(非递增)关系,即使得序列成为一个按关键字有序得序列{rp1,rp2,...,rpn},这样的操作称为排序。
排序得依据是关键字之间的大小关系,这个关键字可以是主关键字也可以是次关键字,同时多个关键字得排序最终都可以转换为单个关键字得排序。
排序得稳定性:假设ki=kj(1<=i<=n,1<=j<=n,i不等于j),且在排序前的序列中ri领先于rj(即i<j)。如果排序后仍然ri领先于rj,则称排序方法是稳定的,反之,若可能使得排序后rj领先于ri,则称所用的排序是不稳定的。
内排序:在排序整个过程中,待排序的所有记录全部被放置在内存中。
外排序:由于排序的记录个数太多,不能全部放在内存中,需要内外存之间多次交换数据才行。
内排序的性能影响因素:
1.时间性能
2.辅助空间
3.算法的复杂性
内排序分为:插入排序、交换排序、选择排序、归并排序
以下讲解7中排序算法:
简单算法:冒泡排序 简单选择排序 直接插入排序
改进算法:希尔排序 堆排序 归并排序 快速排序
排序常用结构和函数
#define MAXSIZE 10 //用于排序的数组个数最大值
typedef struct
{
int r[MAXSIZE+1]; //用于存储要排序数组 r[0]用作哨兵或者临时变量
int length; //用于记录顺序表的长度
}
//交换L中的数组r的下标为i和j的值
void swap(SqList *L,int i,int j)
{
int temp = L -> r[i];
L->r[i] = L->[j];
L->r[j] = temp;
}
冒泡排序
基本思想:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止
//对顺序表L做交换排序 普通方法
void BubbleSort0(SqList *L)
{
int i,j;
for(i=1;i<L->length;i++)
{
for(j=i+1;j<=L->length;j++)
{
if(L->r[i]>L->r[j])
{
swap(L,i,j); //交换下标i与j的值
}
}
}
}
//改进1
//在交换过程中不仅将最小的数字排到了第一,还将其余的较小数字排到了前面
void BubbleSort(SqList *L)
{
int i,j;
for(i=1;i<L->length;i++)
{
for(j=L->length-1;j>=i;j--) //j为从后向前循环
{
if(L->r[j]>L->r[i])
{
swap(L,i,j); //交换下标i与j的值
}
}
}
}
//改进2
//
void BubbleSort2(SqList *L)
{
int i,j;
Status flag = True; //用于标记是否发生交换
for(i=1; i<L->length && flag; i++) //flag为false时将退出循环
{
flag = FALSE; //初始化为假
for(j=L->length-1;j>=i;j--) //j为从后向前循环
{
if(L->r[j]>L->r[i])
{
swap(L,i,j); //交换下标i与j的值
flag = True; //有数据交换,设标记为真
}
}
}
}
冒泡排序的时间复杂度为O(n^2)
简单选择排序算法
基本思想:通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第 i (1 <= i <= n)个记录交换之。
//简单选择排序算法
void SelectSort(SqList *L)
{
int i,j,min;
for(i=1;i<L->length;i++)
{
min = i; //当前下标定义为最小值下标
for(j=i+1;j<L->length;j++) //循环之后的数据
{
if(L->r[min]>L->r[j]) //如果有小于当前最小值的关键字
min = j; //将此关键字的下标给min
}
if(i != min) //若min不等于i, 则说明找到最小值 ,交换
swap(L,i,min);
}
}
时间复杂度为O(n^2),但是性能略优于冒泡排序
直接插入排序
基本思想:将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增 1 的有序表。
//直接插入排序算法
void InsertSort(SqList *L)
{
int i,j;
for(i=2;i<L->length;i++)
{
if(L->r[i]<L->r[i-1]) //需要插入有序表
{
L->r[0] = L->r[i]; //设置哨兵,保存待插入值
for(j=i-1;L->r[j]>L->r[0];j--)
L->r[j+1] = L->r[j]; //大于插入值的记录后移
L->r[j+1] = L->r[0]; //插入到正确位置
}
}
}
如果排序记录为随机的,其平均的比较和移动次数约为(n^2)/4,时间复杂度仍为O(n^2),其性能比冒泡和简单选择排序好一点