数据结构(c语言 第二版 严蔚敏)第八章 排序 期末速成版

一些基本的概念

排序的稳定性:就是排序前可能有两个相同值的数字,K1,K2他们的位置是K1在前K2在后,如果排序后还是同样的顺序K1,K2,则称该排序算法是稳定的,否则是不稳定的

内部排序:指的是待排 序记录全部存放在计算机内存中进行排序的过程

外部排序:指的是待排序记录的数 量很大,以致内存一次不能容纳全部记录,在排序过程中尚需对外存进行访问的排序过程。

插入排序

插入排序的基本思想是:每一趟将一个待排序的记录,按其关键字的大小插入已经排好序 的一组记录的适当位置,直到所有待排序记录全部插入为止。

直接插入排序

void InsertSort(SqList &L)

{//对顺序表L进行直接插入排序

 for(i=2;i<=L.length;++i)

if(L.r[i].key<L.r[i-1].key) //“<”,需将r[i]插入有序子表

{

      L.r[0]=L.r[i]; //将待插入的记录暂存到监视哨中

      L.r[i]=L.r[i-1]; //r[i-1]后移

 for(j=i-2; L.r[0].key<L.r[j].key; --j) //从后向前寻找插入位置

       L.r[j+1]=L.r[j]; //记录逐个后移,直到找到插入位置

      L.r[j+1]=L.r[0]; //r[0]即原r[i],插入正确位置

} //if

}

算法的时间复杂度O(n^2)

          空间复杂度O(1)

【算法特点】

(1)稳定排序

(2)算法简便,且容易实现。

(3)也适用于链式存储结构,只是在单链表上无须移动记录,只需修改相应的指针。

(4)更适合于初始记录基本有序(正序)的情况,当初始记录无序、n较大时,此算法时 间复杂度较高,不宜采用。

折半插入排序

void BInsertSort(SqList &L)

{//对顺序表L进行折半插入排序

 for(i=2;i<=L.length;++i)

 {

L.r[0]=L.r[i];//将待插入的记录暂存到监视哨中

low=1;high=i-1;//置查找区间初值

while(low<=high) //r[low..high]中折半查找插入的位置

{

m=(low+high)/2; //折半

 if(L.r[0].key<L.r[m].key) high=m-1; //插入点在前一子表

 else low=m+1; //插入点在后一子表

} //while

for(j=i-1;j>=high+1;--j) L.r[j+1]=L.r[j]; //记录后移

L.r[high+1]=L.r[0]; //r[0]即原r[i],插入正确位置

 } //for

}

折半插入查找在原来的插入查找的基础上进行了折半查找的过程,而不是原来的顺序查找

折半插入查找的时间复杂度依然为O(n^2),虽然使用折半查找减少了比较的次数,但是依然是要每次移动,每次移动的过程中又要依次后移,所以时间复杂度是不变的

空间复杂度依然为O(1),没有新开辟空间

【算法特点】

(1)稳定排序。

(2)因为要进行折半查找,所以只能用于顺序结构,不能用于链式结构。

(3)适合初始记录无序、n较大的情况。

希尔排序

希尔对记录的分组,不是简单地“逐段分割”,而是将相隔某个“增量”的记录分成 一组。

 ① 第一趟取增量d1(d1<…< d2 < d1),所有记录在同一组中进行直接 插入排序为止。

② 第二趟取增量d2(d2<d1),重复上述的分组和排序。

③ 依次类推,直到所取的增量dt = 1(dt <…< d2 < d1),所有记录在同一组中进行直接 插入排序为止。

希尔排序的平均时间复杂度为O(nlog2n)最坏的时间复杂度为O(n^2)

空间复杂度为O(1)

是一个不稳定的排序

交换排序

冒泡排序

void BubbleSort(SqList &L)

{//对顺序表L进行冒泡排序

 m=L.length-1;flag=1; //flag用来标记某一趟排序是否发生交换

 while((m>0)&&(flag==1))

 {

flag=0;//flag置为0,如果本趟排序没有发生交换,则不会执行下一趟排序

for(j=1;j<=m;j++)

 if(L.r[j].key>L.r[j+1].key)

{

flag=1; //flag置为1,表示本趟排序发生了交换

t=L.r[j]; L.r[j]=L.r[j+1]; L.r[j+1]=t;//交换前后两个记录

} //if

--m;

} //while

} //BubbleSort

时间复杂度O(n^2)

空间复杂度O(1)

该算法是稳定的

快速排序

int Partition(SqList &L, int low, int high)

{//对顺序表L中的子表r[low..high]进行一趟排序,返回枢轴位置

 L.r[0]=L.r[low];//用子表的第一个记录作为枢轴记录

 pivotkey=L.r[low].key; //枢轴记录关键字保存在pivotkey

 while(low<high) //从表的两端交替地向中间查找

 {

while(low<high&&L.r[high].key>=pivotkey) --high;

L.r[low]=L.r[high]; //将比枢轴记录小的记录移到低端

while(low<high&&L.r[low].key<=pivotkey) ++low;

L.r[high]=L.r[low]; //将比枢轴记录大的记录移到高端

 } //while

 L.r[low]=L.r[0]; //枢轴记录到位

 return low; //返回枢轴位置

}

void QSort(SqList &L,int low,int high)

{//调用前置初值 :low=1; high=L.length;

 //对顺序表L中的子表L.r[low..high]进行快速排序

 if(low<high){ //长度大于1

pivotloc=Partition(L, low, high); //L.r[low..high]一分为二,pivotloc是枢轴位置

QSort(L, low, pivotloc-1); //对左子表递归排序

QSort(L, pivotloc+1, high);//对右子表递归排序

 }

}

void QuickSort(SqList &L)

{//对顺序表L进行快速排序

 QSort(L,1,L.length);

}

快速排序最好的时间复杂度为Onlog2n)最坏的时间复杂度为On^2)平均的时间复杂度为Onlog2n

空间复杂度:快速排序是递归的,执行时需要有一个栈来存放相应的数据。最大递归调用次数与递归树 的深度一致,所以最好情况下的空间复杂度为O(log2n)最坏情况下为O(n)

【算法特点】

(1)记录非顺次的移动导致排序方法是不稳定的。

(2)排序过程中需要定位表的下界和上界,所以适合用于顺序结构,很难用于链式结构。

(3)当n较大时,在平均情况下快速排序是所有内部排序方法中速度最快的一种,所以其 适合初始记录无序、n较大的情况。

选择排序

简单选择排序

void SelectSort(SqList &L)

{//对顺序表L进行简单选择排序

 for(i=1;i<L.length;++i){ //L.r[i..L.length] 中选择关键字最小的记录

k=i;

for(j=i+1;j<=L.length;++j)

 if(L.r[j].key<L.r[k].key) k=j; //k指向此趟排序中关键字最小的记录

if(k!=i)

{t=L.r[i]; L.r[i]=L.r[k]; L.r[k]=t;} //交换r[i]r[k]

} //for

}

每次选择最小的那个放在已经排好序列的最右边

时间复杂度O(n^2)

空间复杂度O(1)

堆排序

堆排序(Heap Sort)是一种树形选择排序,在排序过程中,将待排序的记录r[1..n]看成一棵 完全二叉树的顺序存储结构,利用完全二叉树中双亲节点和孩子节点之间的内在关系,在当前 无序的序列中选择关键字最大(或最小)的记录    (按完全二叉树进行排序

 

每次取完全二叉树中的最后一个的孩子节点放到树根,跟树根交换位置,然后再进行重新的排序即可,只要理解堆排序的选择即可

然后堆排序的时间复杂度为O(nlog2n)

堆排序也是不稳定的排序

归并排序

归并排序(Merging Sort)就是将两个或两个以上的有序表合并成一个有序表的过程。将两 个有序表合并成一个有序表的过程称为2-路归并,2-路归并最为简单和常用。下面以2-路归并为 例,介绍归并排序算法。 归并排序算法的思想是:假设初始序列含有n个记录,则可将其看成n个有序的子序列,每 个子序列的长度为1,然后两两归并,得到n/2(向上取整)个长度为2或1的有序子序列;再两两归并,如此 重复,直至得到一个长度为n的有序序列为止。

  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值