内排序
一、排序的基本概念
排序,是整理表中的记录,使之按关键字递增(或递减)。
排序过程中,若整个表都在内存中处理,不涉及数据的内、外存交换,则称之为内排序;反之,则称之为外排序。
1.内排序的分类
2.内排序算法的稳定性
表中,存在有多个关键字相同的记录,排序后相同关键字的记录之间的相对次序保持不变,则这种排序方法是稳定的。反之,则不稳定。
3.正反序
表中元素已按关键字排好序,称此表中元素为正序;
表中元素的关键字顺序正好和排好序的顺序相反,反序。
4.内排序数据结构定义
typedef int KeyType; //定义关键字类型
typedef struct //记录类型
{ KeyType key; //关键字项
InfoType data; //其他数据项,类型为InfoType
} RecType; //排序的记录类型定义
二、插入排序
1.直接插入排序
void InsertSort(RecType R[],int n)
{ int i, j; RecType tmp;
for (i=1;i<n;i++)
{ if (R[i].key<R[i-1].key]) //反序时,不反序不用排,直接看for循环后面的
{ tmp=R[i];
j=i-1;
do //找R[i]的插入位置
{ R[j+1]=R[j]; //关键字大于R[i].key的记录,则后移
j--;
} while (j>=0 && R[j].key>tmp.key)
R[j+1]=tmp; //在j+1处插入R[i]
}
}
}
最好(正序):O(n)
最坏(反序):O(n2)
平均情况:比较次数+移动次数,平均:O(n2)
2.折半插入排序
查找采用折半查找方法,称为二分插入排序或折半插入排序。
void BinInsertSort(RecType R[],int n)
{ int i,j,low,high,mid;
RecType tmp;
for (i=1;i<n;i++)
{ if (R[i].key<R[i-1].key]) //反序时
{ tmp=R[i]; //将R[i]保存到tmp中
low=0; high=i-1;
while (low<=high) //在R[low..high]中查找插入的位置
{ mid=(low+high)/2; //取中间位置
if (tmp.key<R[mid].key)
high=mid-1; //插入点在左半区
else
low=mid+1; //插入点在右半区
} //找位置high+1
for (j=i-1;j>=high+1;j--) //记录后移
R[j+1]=R[j];
R[high+1]=tmp; //插入tmp
}
}
}
在R[0…i-1]中查找插入R[i]的位置,折半查找的平均关键字比较次数为log2(i+1)-1。
平均移动元素的次数为i/2+2。
折半插入排序采用折半查找,查找效率提高,即关键字比较次数减少了。
但元素移动次数不变
3.希尔排序
希尔排序的时间复杂度约为O(n1.3)。
即先按间距分组,先将一组组内拍好,再将另一组数据依次插入。
如d=5,则是分5组,依次类推直到d=1为止。
void ShellSort(RecType R[],int n)
{ int i, j, d;
RecType tmp;
d=n/2; //增量置初值
while (d>0)
{ for (i=d;i<n;i++)
{ //对相隔d位置的元素组直接插入排序
tmp=R[i];
j=i-d;
while (j>=0 && tmp.key<R[j].key)
{ R[j+d]=R[j];
j=j-d;
}
R[j+d]=tmp;
}
d=d/2; //减小增量
}
}
希尔排序法是一种不稳定的排序算法。对于排序算法,所谓的不稳定指的就是相同元素在排序过程中被移动。
三、交换排序
两个元素反序时进行交换
1.冒泡排序(或起泡排序)
void BubbleSort(RecType R[],int n)
{ int i,j; RecType temp;
for (i=0;i<n-1;i++)
{
for (j=n-1;j>i;j--) //比较找本趟最小关键字的记录
if (R[j].key<R[j-1].key) //反序
swap(R[j],R[j-1]); //R[j]与R[j-1]交换
}
}
//改进
void BubbleSort(RecType R[],int n)
{ int i,j; bool exchange; RecType temp;
for (i=0;i<n-1;i++)
{
exchange=false;
for (j=n-1;j>i;j--) //比较,找出最小关键字的记录
if (R[j].key<R[j-1].key) //反序,交换
{ swap(R[j],R[j-1]);
exchange=true;
}
if (exchange==false) return; //中途结束算法
}
}
一旦某一趟比较时不出现记录交换,说明已排好序了,就可以结束本算法。
冒泡排序最好时间复杂度为O(n),最坏和平均为O(n2)。
2.快速排序
- 每趟使表的第1个记录(基准)放入适当位置(归位),将表一分为二,对子表按递归方式继续这种划分.
- 直至划分的子表长为0或1(递归出口)。
int partition(RecType R[],int s,int t) //一趟划分
{ int i=s,j=t;
RecType tmp=R[i]; //以R[i]为基准
while (i<j) //从两端交替向中间扫描,直至i=j为止
{ while (j>i && R[j].key>=tmp.key)
j--; //从右向左扫描,找一个小于tmp.key的R[j]
R[i]=R[j]; //找到这样的R[j],放入R[i]处
while (i<j && R[i].key<=tmp.key)
i++; //从左向右扫描,找一个大于tmp.key的R[i]
R[j]=R[i]; //找到这样的R[i],放入R[j]处
}
R[i]=tmp;
return i;
}
快速排序的平均时间复杂度为O(nlog2n)。平均所需栈空间为O(log2n)。
(未完待续…)