数据结构——排序

插入排序,交换排序,选择排序,归并排序

期末复习总结,笔记图片来源于张海清老师的ppt

数据结构

排序

插入排序

直接插入排序

基本思想:是将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数增1的有序表。
一、排序过程:
整个排序过程为n-1趟插入,即先将序列中第1个记录看成是一个有序子序列,然后从第2个记录起逐个进行插入,直至整个序列变成按关键字非递减有序序列为止。
二、算法过程演示及描述:

已知关键字序列为{49 38 65 97 76 13 27 49},采用直接插入排序方法对其进行排序。
初始关键字:(49) 38 65 97 76 13 27 49

i=2: (38) (38 49) 65 97 76 13 27 49

i=3: (65) (38 49 65) 97 76 13 27 49

i=4: (97) (38 49 65 97) 76 13 27 49

i=5: (76) (38 49 65 76 97) 13 27 49

1-6: (13) (13 38 49 65 76 97) 27 49

i=7: (27) (13 27 38 49 65 76 97) 49

1-8: (49) (13 27 38 49 49 65 76 97)

//对顺序表L作直接插入排序
void InsertSort(SqList &L)
{ for ( i = 2 ; i < = L . length ; + + i )
    if(L.r[i].key<L.r[i-1].key)
    	{L.r[0]=L.r[i];//r[0]作为哨兵单元,暂存元素
    	 L.r[i]=L.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];//插入到正确位置
    }
}

三、算法评价(稳定排序)

对象个数为n,则执行n-1躺

最好情况下:每趟只需比较1次,不移动,总比较次数为n-1

平均情况比较次数和移动次数为n²/4

时间复杂度:O(n²)

空间复杂度:O(1)

折半排序插入法

一、排序过程:用折半查找方法确定插入位置的排序叫~

二、算法过程演示及描述:

​ 在插入 r[i] 时,利用折半查找法寻找 r[i] 的插入位置
在这里插入图片描述

三、算法评价(稳定排序)

时间复杂度:T(n)=O(n²)

空间复杂度:S(n)=O(1)

折半插入排序和直接插入排序相比,折半插入排序仅减少了关键字比较次数,而记录的移动次不变。其时间复杂度仍为O(n²)。

希尔排序

基本思想:先将整个待排记录序列分割成若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。

一、排序过程:
先取一个正整数d1<n,把所有相隔d1的记录放到一组组内进行直接插入排序;然后取d2<d1,重复上述分组和排序操作;直至di=1,即所有记录放进一个组中排序为止。
二、算法过程演示及描述:

初始:49 38 65 97 76 13 27 48 55 4
在这里插入图片描述
步长最好为质数

void ShellSort(SqList L){
	for(d=L.length/2;d>=1;d=d/2)
		for(i=d+1;i<=L.length;i++)//完成一趟Shell排序
            if(L.r[i].key<L.r[i-d].key){//寻找插入位置
				L.r[0]=L.r[i];//暂存在L.r[0]中
				for(j=i-d;j>0&&L.r[0].key<L.r[j].key;j-=d)
					L.r[j+d]=L.r[j];//记录数据向后搬移
				L.r[j+d]=L.r[0];//插入
            }
}

性能分析:时间复杂度:O(n3/2);空间复杂度:O(1)。

void ShellInsert(SqList L, int d){
	for(k=1;k<=d;k++)//O(d),d组子序列分别排序
        for(i=d+k;i<=L.length;i+=d){//O(n/d)
            e=L.r[i];
			for(j=i-d;j>0&&e.key<L.r[j].key;j-=d)
				L.r[j+d]=L.r[j];//O(n/d)
			L.r[j+d]=e;
}}//O(n*n/d)
void ShellSort(SqList &L,int dlta[],intt){
    for(k=0;k<t;k++)ShellInsert(L,dlta[k]);
}

希尔排序的特点:(不稳定排序)

1.子序列的构成不是简单的“逐段分割”,而是将相隔某个增量的记录组成一个子序列。

2.希尔排序可提高排序速度,因为

①分组后n值减小,n²更小,而T(n)=O(n²),所以T(n)从总体上看是减小了;

②关键字较小的记录跳跃式前移,在进行最后一趟增量为1的直接插入排序时,序列已基本有序。

3.增量序列(即步长)取法

①无除1以外的公因子;

②最后一个增量值必须为1。

步长的取法:d1=n/2

​ d2=d1/2 ………

直到di=1为止

交换排序

冒泡排序 (Bubble Sort)

(一)排序过程:

1.首先将第一个记录的关键字与第二个记录的关键字进行比较,若为逆序(L.r[1].key>L.r[2].key) ,则交换两个记录,然后比较第二个记录与第三个记录的关键字。依次类推,直至第n-1个记录和第n个记录的关键字比较过为止——上述过程称为第一趟冒泡排序,结果使关键字最大的记录被安置在最后一个记录的位置上。

2.然后对前n-1个记录进行第二趟冒泡排序,结果使关键字次大的记录被安置在第n-1个记录的位置上。

3.重复上述过程,直到“在一趟排序过程中没有进行过交换记录的操作”为止。

(二)算法过程演示:

初始关键字序列:49 38 65 97 76 13 27 49

第一趟排序后:38 49 65 76 13 27 49 97

第二趟排序后:38 49 65 13 27 49 76

第三趟排序后:38 49 13 27 49 65

第四趟排序后:38 13 27 49 49

第五趟排序后:13 27 38 49

第六趟排序后:13 27 38

第七趟排序后:13 27

起泡排序

基本思想:
每趟不断将记录两两比较,并按“前小后大”规则交换
21,25, 49, 5*, 16, 08
21,25, 25*, 16, 08, 49
21, 25, 16, 08, 25*, 49
21,16,08, 25, 25*, 49

16, 08, 21, 25, 25*, 49
08,16, 21, 25, 25*, 49

优点:
每趟结束时,不仅能挤出- -个最大值到最后面位置,还能同时部分理顺其他元素;一旦下趟没有交换,还可提前结束排序

void bubble sort(SqList &L)
{ int m,i,j,flag=1; RedType X;
	m=n-1;
	while((m>0)&(lag==1)))
	{ flag=0;
		for(j=1;j<=m;j++)
			if(L.r[j].key>L.[j+1].key)
			{ flag=1;
				x=L.r[j];L.r[j]=L.r[j+1];L.r[j+1]=x; //交换
			}//endif
		m--;
	}//endwhil.
}

•设对象个数为n

•比较次数和移动次数与初始排列有关

最好情况:只需 1趟排序,比较次数为 n-1,不移动

算法评价(稳定排序)

时间复杂度:T(n)=O(n²)

空间复杂度:S(n)=O(1)
①最好情况(正序)
关键字比较次数: n-1
记录移动次数:0
②最坏情况(逆序)
关键字比较次数: 1/2(n(n-1))
记录移动次数:3/2(n(n-1))

快速排序(Quick Sort)—— 霍尔排序

基本思想:
1.通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录进行排序以达到整个序列有序。
★快速排序实际上是对冒泡排序的一-种改进。
★改进的着眼点:在冒泡排序中,记录的比较和移动是在
相邻单元中进行的,记录每次交换只能上移或下移一个单
元,因而总的比较次数和移动次数较多。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

int QuickOne(SqList L, int i, int j){
	L.r[0]=Lr[i]; e=L.r[i].key; //e是控制关键字
	while(i<){ //i=j时,找到e的最终位置
		while(e <=L.r[j].key && i<j) j--; //与后比较
		L.r[i]=L.r[j]; 
		while(e >=L.r[i].key && i<j) i++; //与前比较
		L.r[j]=L.r[i];
		}//while
	L.r[i]=L.r[0];
	return i; //返回枢轴位置
} //end

//递归
void main ( )
 {   QSort ( L, 1, L.length ); }

void QSort ( SqList &L,int low,  int  high ) 
{  if  ( low < high ) 
    {  pivotloc = Partition(L, low, high ) ;
        Qsort (L, low, pivotloc-1) ; 
        Qsort (L, pivotloc+1, high ) 
     }
}
int Partition ( SqList &L,int low,  int  high ) 
{  L.r[0] = L.r[low];   pivotkey = L.r[low].key;
   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];
     }
    L.r[low]=L.r[0]; 
    return low;
}

可以证明,平均计算时间是O(nlog₂n)。

最大递归调用层次数与递归树的深度一致,因此,要求存储开销为 O(log2n) 。

算法评价:(不稳定排序)

时间复杂度:

① 最好情况(每次总是选到中间值作枢轴)**T(n)=O(log2n)

② 最坏情况(每次总是选到最小或最大元素作枢轴)T(n)=O(n²)

空间复杂度:需栈空间以实现递归

最坏情况:S(n)=O(n)

一般情况:S(n)=O(log 2 n)

快速排序是对冒泡排序的一种改进方法,算法中元素的比较和交换是从两端向中间进行的,关键字较大的元素一次就能够交换到后面单元,关键字较小的元素一次就能够交换到前面单元,记录每次移动的距离较远,因而总的比较和移动次数较少。

选择排序

简单/直接选择排序 (Simple/Straight Selection Sort)

一、排序过程:

首先通过n-1次关键字比较,从n个记录中找出关键字最小的记录,将它与第一个记录交换

再通过n-2次关键字比较,从剩余的n-1个记录中找出关键字次小的记录,将它与第二个记录交换

重复上述操作,共进行n-1趟排序后,排序结束

二、算法过程演示及描述:
在这里插入图片描述

//对顺序表L作简单选择排序
void  SelectSort ( Sqlist  &L) 
  {  for ( i=1; i<L.length; ++i ) {	 //选择第i小的记录,并交换到位
		//在L.r[i…L.length]中选择最小的记录
		j=i;
		 for ( k=i+1; k<=L.length; ++k )
		      if  (L.r[k].key<L.r[j].key)	
			j=k; 		//找到关键字最小的记录,用j记下位置
	      	if ( i != j )  {		 //第j个记录与第i个记录交换
 		    L.r[0]=L.r[j];  
                  L.r[j]= L.r[i];
                  L.r[i]=L.r[0]; } 
        }
}

三、算法评价**(不稳定排序)**

时间复杂度:T(n)=O(n²)

关键字比较次数:1/2(n*(n-1))

记录移动次数

最好情况(正序):0

最坏情况(逆序):**3(**n-1)

空间复杂度:S(n)=O(1)

堆排序

在这里插入图片描述

将无序序列建成一个堆,得到关键字最小(或最大)的记录;得到堆顶的最小(或最大)值后,使剩余n-1个元素的序列重又建成一个堆,则得到n个元素的次小值(或次大值)。如此重复执行,便得到一个有序序列,这个过程称之为堆排序(降序)(或升序)。

三、算法过程演示及描述:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

HeapSort(SqList  L){
       for( i=L.length/2 ; i>0 ; i--)
              HeapCreate( L , i , L.length );
       for( i=L.length ; i>1 ; i-- ) {
              L.r[1] L.r[i];
              HeapCreate(L , 1 , i-1);
             }   
}
 HeapCreate( SqList  L , int  s , int len ){
     e=L.r[s];
     for(j=2*s ;j<=len ; j*=2){
         if(j<len && L.r[j].key<L.r[j+1].key) j++;
         if(e.key>=L.r[j].key)break;	//无须调整
    /* 大的数据往上提,s  往下移 ,继续调整*/
          L.r[s]=L.r[j] ;   s =j ;}
     L.r[s]=e ; 
  } 

四、算法评价(不稳定排序)

时间复杂度:最坏情况下 **T(n)=O(**nlogn)

空间复杂度:S(n)=O(1)

堆经常用来实现优先队列和外排序算法。

归并排序

2-路归并排序

排序过程

设初始序列含有n个记录,则可看成是n个有序的子序列,每个子序列的长度为1

然后两两归并,得到 [n/2]个长度为2或1的有序子序列

再两两归并,……如此重复,直至得到一个长度为n的有序序列为止

算法演示及描述:

在这里插入图片描述

//两两归并
Merge( int  a[ ], int  b[ ] , int  i ,  int n , int m){
   for(j=n+1 , k=i ; i<=n  && j<=m ; k++){
        if(a[i]<=a[j])  b[k]=a[i++];
        else  b[k]=a[j++];
   }
   if(i<=n) for( ; i<=n;i++,k++)     b[k]=a[i];
   if(j<=m)for( ; j<=m ; j++,k++)  b[k]=a[j];
}  //O(两个归并段长度之和 )

归并排序的非递归算法

typedef struct {int  *elem;   int   len;}SqList; 
void  MergeSort(  SqList  &L){  h=1; 	//h是归并段长
        B=(int*)malloc(L.len*sizeof(int));
        while( h <(L.len)){i=0;
         while((i+h)<(L.len)){ 	//一趟归并排序
              if((i+2*h)<(L.len))  m=i+2*h-1;
              else   m=(L.len)-1 ;
              Merge(L.elem , B , i , i+h-1 , m);	//O(2*h)
              i=i+2*h;}           	//O( L.len/(2*h) )
         for(i=0;i<=m;i++) L.elem[i]=B[ i ];
         h=2*h; } 		//O(log n) (n=L.len) 
         free( B );  }		//O(n*log n )

算法评价:(稳定排序)

时间复杂度:T(n)=O(nlog 2 n)

空间复杂度:S(n)=O(n)

在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值