数据结构与算法----表排序与归并排序

表排序

表排序使用场景

当我们的排序对象不再是整数或者其他基本类型时,比如是一个含有多个数据元素的结构体,想象成一本书或者一个视频,其所占用的内存就很大,在其他的排序算法中总会涉及数据的频繁交换,但是交换一个庞大的结构体就会很不方便,此时我们可以采用表排序

算法概述

定义一个table表,用于装结构体对象数组info[]的指针(即数组下标索引),然后对索引以对象数组为比较对象进行table数组的排序
,每次的比较对象是==info [ table[ i ] ] ==,因为当前table的值即相应info数组的索引,对table数组的排序可以采用插入排序、选择排序或者冒泡排序等基本排序方法
最后按照排序好的table[] 索引数组输出info对象数组

代码实现

void table_sort(Info *info,int n)
{
	int table[n];
	for(int i=0;i<n;i++)    //定义table表 
		table[i]=i;
	for(int i=1;i<n;i++)   //对table表进行选择排序 
	{
		for(int j=i-1;j>=0;j--)
		{
			int t=table[i];   //将当前需要插入到已排好序的数组中的数据保存下来 
			if(info[table[j]].key<info[table[i]].key)   
			{
				for(int k=i;k>j+1;k--)    //从当前元素的下一个元素依次将元素往后移动一位 
				{
					table[k]=table[k-1];
				} 
				table[j+1]=t;
				break;
			}
			else if(j==0)         //如果当前元素比前面排好序的所有元素都要小,进行不同的插入操作 
			{
				for(int k=i;k>j;k--)
				{
					table[k]=table[k-1];
				} 
				table[j]=t;	
			}
		}
		for(int i=0;i<n;i++)
			cout<<table[i]<<"  ";
		cout<<endl;
	}
	for(int i=0;i<n;i++)
	{
		cout<<info[table[i]].key<<"    "<<info[table[i]].name<<endl;
	}    //按照排好序的table表输出info对象数组 
}

物理排序

当我们用表排序将table排好序后,输出的时候是依赖于table表输出的,如果要正直的实现info对象数组的排序,即可以直接根据i从0-n输出info数组,就需要在表排序的基础上进行物理排序
可以看出n个数字的排列由若干个独立的环组成
依赖每个环单独地排序,便可将info正确的排好序
表排序与物理排序

归并排序

归并排序算法 思想

归并排序详情
个人觉得这篇文章写得很好,可以看看这个的讲解

递归实现代码

//有序子列的归并
//L=左边起始位置,R=右边起始位置,RightEnd=右边终点位置
//为了保证函数传入的参数足够小,但是又必须足够,传入这三个参数,剩下的通过计算推理得到 
void Merge(ElementType A[],ElementType TmpA[],int L,int R,int RightEnd)
{
	int LeftEnd=R-1,  //左边终点的位置,假设左右两个子列是挨着的 
	Tmp=L,            //存放结果数组的初始位置 
	NumElements=RightEnd-L+1;   //存放结果数组元素个数
	while(L<=LeftEnd&&R<=RightEnd)
	{
		if(A[L]<A[R])  TmpA[Tmp++]=A[L++];
		else           TmpA[Tmp++]=A[R++];
	} 
	while(L<=LeftEnd)   TmpA[Tmp++]=A[L++];
	while(R<=RightEnd)   TmpA[Tmp++]=A[R++];
	for(int i=0;i<NumElements;i++,RightEnd--)
	{
		A[RightEnd]=TmpA[RightEnd];     //最后将TmpA数组中的元素导回到A数组中 
	} 
}
void Msort(ElementType A[],ElementType TmpA[],int L,int RightEnd)
{
	if(L>=RightEnd)  return;   //当子序列中没有元素时不再进行递归排序
	int Center=(L+RightEnd)/2; 
	Msort(A,TmpA,L,Center);
	Msort(A,TmpA,Center+1,RightEnd);
	Merge(A,TmpA,L,Center+1,RightEnd); 
}
//统一函数接口(所有排序函数的接口都统一为 排序名字_sort) 
void Merge_sort(ElementType A[],int n)
{
	ElementType *TmpA;
	TmpA=(ElementType *)malloc(n*sizeof(ElementType));
	if(TmpA!=NULL)
	{
		Msort(A,TmpA,0,n-1);
		free(TmpA);   //养成好的习惯,用完一块内存就释放掉 
	}
	else cout<<"空间不足"<<endl;
}

程序中TmpA数组在最外面声明,并且每次调用递归函数都传入数组头指针,避免了将该数组在merge函数中声明的情况,那样会导致每次递归调用都在执行数组的开辟和释放

非递归实现代码

递归算法虽然简单容易理解,但是会使用额外的堆栈,程序执行效率不是很高,所以会想采用非递归算法

思路:
在这里插入图片描述
为了实现不需要每次归并都定义一个数组,我们采用两个数组来回导的方式,这样额外的空间复杂度只需要O(n)

/* 归并排序 - 循环实现 */
/* 这里Merge函数与递归版本的Merge不太一样,不需要最后一步将TmpA导回A */
void Merge(ElementType A[],ElementType TmpA[],int L,int R,int RightEnd)
{
	int LeftEnd=R-1,  //左边终点的位置,假设左右两个子列是挨着的 
	Tmp=L,            //存放结果数组的初始位置 
	NumElements=RightEnd-L+1;   //存放结果数组元素个数
	while(L<=LeftEnd&&R<=RightEnd)
	{
		if(A[L]<A[R])  TmpA[Tmp++]=A[L++];
		else           TmpA[Tmp++]=A[R++];
	} 
	while(L<=LeftEnd)   TmpA[Tmp++]=A[L++];
	while(R<=RightEnd)   TmpA[Tmp++]=A[R++];
}

// length = 当前有序子列的长度
void Merge_pass( ElementType A[], ElementType TmpA[], int N, int length )
{ /* 两两归并相邻有序子列 */
     int i, j;
      
     for ( i=0; i <= N-2*length; i += 2*length )
         Merge( A, TmpA, i, i+length, i+2*length-1 );    //保证最后的结果一定是存在TmpA数组中的 
     if ( i+length < N ) /* 归并最后2个子列*/       //最后剩下的子序列可能是两个,也可能是单独的一个,需要单独讨论 
         Merge( A, TmpA, i, i+length, N-1);
     else /* 最后只剩1个子列*/
         for ( j = i; j < N; j++ ) TmpA[j] = A[j];
}

void Merge_Sort( ElementType A[], int N )
{ 
     int length; 
     ElementType *TmpA;
     
     length = 1; /* 初始化子序列长度*/
     TmpA = (ElementType *)malloc( N * sizeof( ElementType ) );
     if ( TmpA != NULL ) {
          while( length < N ) {
              Merge_pass( A, TmpA, N, length );    //每次循环执行两次,先将A导入TmpA,再将TmpA导入A 
              length *= 2;
              Merge_pass( TmpA, A, N, length );    //最后一次归并如果归并结果就在A里面,这一步就只是简单的复制了一下数组而已 
              length *= 2;
          }
          free( TmpA );
     }
     else printf( "空间不足" );
}

归并排序优点和缺点

优点:
归并排序稳定并且始终能保证时间复杂度为O(nlogn)
缺点:
唯一不好的缺点就是在程序中需要在两个数组中来回导,不适合用于内排序,基本上内排序都不会想用归并排序,归并排序更适合外排序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值