内部排序(插入排序、希尔排序、冒泡排序)
1.排序的基本概念
(1)排序
将数据元素的无序序列调整为有序序列的过程。
(2)稳定性
假设记录R和S关键字相同,且排序前R在S之前,如果排序后R还在S之前,就称算法是稳定的。
(3)内部排序
在排序的整个过程中,待排序的所有记录全部被放置在内存中处理的排序方法。
(4)八大内部排序
冒泡排序、插入排序、选择排序、希尔排序、快速排序、归并排序、基数排序、堆排序
(5)内部排序的分类
按时间复杂度可分为
a.简单排序方法:时间复杂度位O(n^2)
b.先进排序方法:时间复杂度为O(nlogn)
c.基数排序,其时间复杂度为O(d*n)
按依据原则
a.插入类: 直插排序、二分排序、希尔排序
b.交换类:冒泡排序、快速排序
c.选择类:直接选择排序、树形排序、堆排序
d.归并类:二路归并排序、多路归并排序
e.分配类:多关键字排序、基数排序
(6)排序算法的性能
a.内部排序的基本操作
比较:关键码之间的比较
移动:记录从一个位置移动到另一个位置
b.辅助空间
指除了存放待排序记录占用的存储空间之外,执行算法所需要的其他存储空间。
2.插入排序
(1)基本思想
先将序列中的第1个记录看成一个有序的子序列,然后从第2个记录起逐个插入,直至整个序列变成按关键字非递减有序序列为止。整个排序过程需进行n–1趟排序。
(2)直接插入的过程示例
r[0]的作用是 监视哨和暂存单元
(3)直接插入的算法
// 对L.r[1..L.length]作直接插入排序
void InsertionSort ( SqList &L ) {
for (int i=2; i<=L.length; ++i ) { //第2个到第length个记录依次插入
if (L.r[i].key < L.r[i-1].key){
L.r[0] = L.r[i]; // 复制为监视哨
for ( j=i-1; L.r[0].key < L.r[j].key; --j ) //从后往前查找r[i]的正确插入位置
L.r[j+1] = L.r[j]; // 大于待插入记录的记录后移
L.r[j+1] = L.r[0]; // 插入到正确位置
}
}
}
(4)直接插入的算法性能分析
a.空间复杂度:只需要一个记录的辅助空间。O(1)
b.时间复杂度:
假设需要排成升序序列
最好情况下:序列本来是升序的 所以比较次数为n-1,移动次数为0;则时间复杂度为O(n).
最坏情况下:序列本来是降序的 比较次数为(n-2)(n-1)/2,移动次数为(n+4)(n-1)/2,则时间复杂度为O(n^2).
取均值时间复杂度为O(n^2)
c.直接插入算法是稳定的
d.算法简单、容易实现,适用于待排序记录基本有序或待排序记录较小时。
(5)直接插入排序的改进——折半插入排序
a.基本思想
利用折半查找查找“ L.r[1…i-1]中 L.r[i] 的插入位置”的插入排序为折半插入排序。
b.折半插入的算法
void BinsertSort(SqList &L){
int i,low,high,mid;
for(i=2; i<= L.length; ++i) {
L.r[0]=L.r[i];
low=1; high=i-1;//直接修改直接插入排序算法的部分
while(low<=high) {
mid=(low+high)/2;
if (L.r[0].key < L.r[mid].key) high=mid-1;
else low=mid+1;
}
for(int j=i-1; j>=low;--j ) L.r[j+1]=L.r[j];
L.r[low]=L.r[0];
}
}
c.折半插入排序性能分析
折半插入减少了关键字比较次数,移动次数不变,时间复杂度也是O(n^2),但就平均性能而言折半插入优于直接插入
折半插入也是稳定的
3.希尔排序
(1)基本思想
先将整个表分割成若干个子序列,每个子序列由相隔某个“增量”的元素组成, 各个子序列分别进行直接插入排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。
(2)希尔排序的过程示例
(3)希尔排序算法
//一趟希尔插入排序。
void ShellInsert (SqList &L, int dk )
{
//1.前后记录位置的增量是dk.
//2.L.r[0]只是暂存单元,不是哨兵。当j<=0时,插入位置已找到
//先插第1子序列的第2个记录,再插第2子序列的第2个记录…
for (int i=dk+1; i<=L.length; ++i )
if ( L.r[i].key< L.r[i-dk].key) {//将R[i]插入有序增量子表
L.r[0] = L.r[i]; // 暂存待插入记录在R[0]
for (j=i-dk; j>0 && (L.r[0].key< L.r[j].key);j -= dk)
L.r[j+dk] = L.r[j]; // 记录后移dx个位置,查找插入位置
L.r[j+dk] = L.r[0]; // 插入
}
}//ShellInsert
(4)希尔排序算法分析
研究表明,希尔排序的时间性能在O(n2)和O(nlog2n)之间。有人通过大量的实验,给出了目前较好的结果:当n较大时,比较和移动的次数约在n1.23到1.6n1.25之间,即约为O(n1.3 ) 。
4.冒泡排序
(1)冒泡排序的基本思想
两两比较相邻记录的关键码,如果反序则交换,直到没有反序的记录为止。小的浮起,大的沉底
(2)冒泡排序的示例
- 在这里插入图片描述
(3)冒泡排序的算法
void BubbleSort(SqList &L)
{
for (int i = L.length-1; i>0; --i) //i表示趟数,最多n-1趟
{
for (int j = 0; j < i; ++j)
if (L.R[j] > L.R[j+1]) //逆序
{
temp=L.R[j];
L.R[j]=L.R[j+1];//交换
L.R[j+1]=temp;
}
}
}
(4)冒泡排序的时间性能分析
冒泡排序的时间复杂度为O(n^2),算法是稳定的,适用于数据较少的情况。
mp=L.R[j];
L.R[j]=L.R[j+1];//交换
L.R[j+1]=temp;
}
}
}
**(4)冒泡排序的时间性能分析**
冒泡排序的时间复杂度为O(n^2),算法是稳定的,适用于数据较少的情况。