简单排序 冒泡,选择,快排,插入,归并

由于是不同时间的组合,冒泡,选择,快排,插入 有一点外部数据外部处理,内部数据内部处理,为了更好的突出这点,理应在参数列表里加上const,这样更明显一些,多出来t 为左起点,len为元素个数,而 归并没有进行处理,直接使用自带的left 和right

冒泡 bubble_sort.h

#pragma once
//冒泡排序
//鱼吐泡泡,比较大的数一点一点向最大的方向移动
template<typename T>
void bubble_sort(T *srcData,int left,int right)
{
	int t = left;
	int len = right - left;
	for (int i = 0; i < len-1; i++)			//c0 第一次初始化  c1 判断 c2 自增
	{
		for (int j = 0; j < len - i - 1; j++)	//c3 初始化  c4判断  c5自增
		{
			if (srcData[t + j]>srcData[t + j + 1])	//c6判断
			{
				T temp = srcData[t + j];	//3*c7  交换
				srcData[t + j] = srcData[t + j + 1];
				srcData[t + j + 1] = temp;
			}
		}
	}
}

//c0 8第一次初始化  c1 8判断 c2 8自增 c3 10初始化  c4 10判断  c5 10自增  c6  12判断   3*c7  14-16交换
//全都顺序
//没有 c7     8循环n-1次     c4  (n+2)(n-1)/2    c5,c6  (n-1)*n/2
//t= c0 + c1*n + c2*(n-1) + c3*(n-1)+ c4*(n...2)  +c5*((n-1)...1)+c6*((n-1)...1)
//	8						10										12
//t=(c4/2+c5/2+c6/2)n^2+(c1+c2+c3+c4/2-c5/2-c6/2)n+(c0-c2-c3-c4)
//全都逆序
//多了 (n-1)*n/2次的 3c7
//t=(c4/2+c5/2+c6/2+3*c7/2)n^2+(c1+c2+c3+c4/2-c5/2-c6/2-3*c7/2)n+(c0-c2-c3-c4)

选择排序 select_sort.h

#pragma once
//选择排序
//从一堆牌里面找到最小的那张,一张一张取出来
template<typename T>
void select_sort(T *srcData, int left, int right)
{
	int t = left;
	int len = right - left;
	for (int i = 0; i < len - 1; i++)			//c0初始化 c1判断 c2自增
	{
		T temp = i;								//c3初始化
		for (int j = i + 1; j < len; j++)		//c4初始化 c5判断 c6 自增
		{
			if (srcData[t + temp]>srcData[t + j])	//c7判断
				temp = j;						//c8 赋值
		}
		T tempVal=srcData[t+temp];				//c9 swap
		srcData[t+temp]=srcData[t+i];
		srcData[t+i]=tempVal;
		//发现一张比较小的牌,就记住他的位置,看完所有剩余的牌,最后取出来

#if 0
		//发现一张比较小的牌就把他取出来,如果不是最小的牌就把他再放到牌堆里面,然后把那张更小的牌取出来
		for (int j = i + 1; j < len; j++)
		{
			if (srcData[t+i]>srcData[t+j])
			{
				T temp = srcData[t+i];
				srcData[t+i] = srcData[t+j] ;
				srcData[t+j] = temp;
			}
		}
#endif
	}
}

//c0 8第一次i初始化,c1 8i的判断,c2 8i自增,c3 10temp赋值,c4 11j初始化,c5 11j判断,c6 11j自增,c7 13判断,c8 14赋值  c9 交换
//全都顺序
//没有 c8     11   tj   n...2次判断    (n-1)...1自增    13 (n-1)..1判断 
// t=c0+c1*n+c2*(n-1)   +	c3*(n-1)	+c4*(n-1)	+c5*(2+n)*(n-1)/2	+c6*n*(n-1)/2+	c7*n*(n-1)/2		+c9*(n-1)
//		8					10			11												13					16
//t=(c5/2+c6/2+c7/2)*n^2+(c1+c2+c3+c4+c5/2-c6/2-c7/2)*n+(c0-c2-c3-c4-c5-c9)
//全都逆序
//有c8  赋值 n-1...1次
//     1...n        1...n-1        1...n-1
// t=c0+c1*n+c2*(n-1)   +	c3*(n-1)	+c4*(n-1)	+c5*(2+n)*(n-1)/2	+c6*n*(n-1)/2+	c7*n*(n-1)/2+	c8*n*(n-1)/2		+c9*(n-1)
//		8					10			11												13				14					16
//t=(c5/2+c6/2+c7/2+c8/2)*n^2+(c1+c2+c3+c4+c5/2-c6/2-c7/2-c8/2)*n+(c0-c2-c3-c4-c5-c9)

快排 quick_sort.h

#pragma once
//快速排序
//随便拿一张牌,然后把比这张牌小的扔一起,比这张牌大的扔到一起。对小堆,和大堆重复分牌操作
template<typename T>
void quick_sort(T *srcData, int left, int right)
{
	int t = srcData[left];
	int b = left + 1, e = right-1;
	if (b > e)				//确定牌堆里面还有牌
		return;
	while (b <= e)				//只要还有牌
	{
		while (b<=e&&t >= srcData[b])		//只要还有牌,把连续小的扔一起,找到第一张应该在大牌堆里的牌,即这张牌左边都是小牌
			b++;
		while (b<=e&&t <= srcData[e])		//只要还有牌,把连续大的扔一起,找到第一张应该在小牌堆里的牌,即这张牌右边都是大牌
			e--;
		if (b<e)		//只要还有牌,把这两张特殊的牌放到各自应该在的牌堆里面
		{
			T temp = srcData[b];
			srcData[b] = srcData[e];
			srcData[e] = temp;
			b++;
			e--;
		}
	}
	srcData[left] = srcData[b-1];		//这张牌是最后一张小牌,是中间位置
	srcData[b-1] = t;
	quick_sort(srcData, left, b-1);		//把小牌堆进行重复操作
	quick_sort(srcData, b, right);		//把大牌堆进行重复操作
}

插入排序 insert_sort.h

#pragma once
//插入排序     打牌的顺牌操作,摸了一张牌,找到它应该在的位置,放进去
//假设每步指令处理时间都是相同的1,输入规模n=10,sigma累加
template<typename T>
void insert_sort(T srcData[], int left, int right)
{
	int t = left;		
	int len = right - left;
	for (int i = 1; i < len; i++)			//10次判断,9次自增--》9次循环
	{
#if 0
		//way a srcData		把这张牌从后往前一点一点移动
		for (int j = i; j > 0; j--)
		{
			if (srcData[j + t] < srcData[j + t - 1])
			{
				T temp = srcData[j + t];
				srcData[j + t] = srcData[j + t - 1];
				srcData[j + t - 1] = temp;
			}
		}
#endif 
#if 0
		//way b		把这张牌,找到位置,然后放进去
		T temp = srcData[t + i];				//9次赋值
		int j=i;							//9次初始化
		for (; j > 0 && srcData[j + t - 1] > temp; j--)			//sigma tj	tj表示对这个j值执行循环的次数   9次赋值j=i,
			srcData[j + t] = srcData[j + t - 1];	//sigma (tj-1)
		srcData[j + t] = temp;			//9次赋值
#endif
//#if 0
	//way c 二分查找找位置,但由于赋值缓慢,最差结果为从头开始一个一个添加数据,n^2 ,最顺利结果,不需要赋值,时间全花在了查找上,为n*lgn
	T temp = srcData[t + i];	
	int b=0,e=i,mid;
	while(b+1<e)
	{
		mid=(b+e)>>1;
		if(srcData[t+mid]>temp)
			e=mid;
		else
			b = mid;
	}    //最后定位结果是b这个位置
	if (srcData[b] > temp)    //如果b这个位置比目标值大,那就在前面插入,主要是初始化第一次插入时,不论结果如何,b都为0
		b--;
	int j = i;
	for (; j-1 > b; j--)	
		srcData[j + t] = srcData[j + t - 1];
	srcData[j + t] = temp;
	}
//#endif
}

//c0 9第一次i初始化,c1 9i的判断,c2 9i自增,c3 24temp赋值,c4 25j初始化,c5 26j判断,c6 26j自减,c7 27赋值,c8 28赋值
//全都顺序
//26 只需要进行判断,次数1, 没有自减,没有27
// t=c0+c1*n+c2*(n-1)   +	c3*(n-1)	+c4*(n-1)	+c5*(n-1)			+c8*(n-1)
//		9					24			25			26						28
//t=(c1+c2+c3+c4+c5+c8)*n+(c0-c2-c3-c4-c5-c8)
//全都逆序
//26  判断j+1次   ,自减j次    27   j次赋值
//     1...n        1...n-1        1...n-1
//t=c0+c1*n+c2*(n-1)   +	c3*(n-1)	+c4*(n-1)	+ c5*(1+n)*n/2	+c6*n*(n-1)/2  + c7*n*(n-1)/2	+c8*(n-1)
//t=(c5/2+c6/2+c7/2)n^2+(c1+c2+c3+c4+c5/2-c6/2-c7/2+c8)*n+(c0-c2-c3-c4-c8)

归并排序 merge_sort.h

#pragma once
//归并排序
//可以理解为建立在插入排序的基础之上
//插入排序是一张一张未知牌进行找位置,归并排序是一堆一堆已知顺序的牌进行找位置
template<typename T>
void merge(T arr[], int left, int mid, int right)		
{
	int n1 = mid - left;
	int n2 = right - mid;
	T *arrL = new T[n1];
	T *arrR = new T[n2];
	for (int i = 0; i < n1; i++)			//记录已经整理好的左牌段
		arrL[i] = arr[left + i];
	for (int i = 0; i < n2; i++)			//记录已经整理好的右牌段
		arrR[i] = arr[mid + i];
	//相当于把牌段里的所有牌先都取出来,然后再按照正确的顺序,一点一点放进去
#if 0

	// 方法一 增加一个最大值以确定结尾,但是作为模板,这个最大值并不确定,这里直接要求所有的排序都为int,同时有出现值本来就是最大值的可能,这种增加值以确定结尾的方法并不安全,但是书写快
	arrL[n1] = (unsigned int)(-1) >> 1;  
	arrR[n2] = (unsigned int)(-1) >> 1;
	int i, j, k;
	for (k = left, i = 0, j = 0; k < right; k++)    //对数据直接进行修改,确定要整理的牌段 k 从left到right
	{
		if (arrL[i] < arrR[j])		//取出各牌段的最左边的那张牌,把小的那张牌放到正确位置上,然后取出下一张牌
		{
			arr[k] = arrL[i];
			i++;
		}
		else
		{
			arr[k] = arrR[j];
			j++;
		}
	}
#endif

//#if 0

	//方法二 计数已经排了多少个数
	int i=0, j=0, k=left;
	while (k < right&&i < n1&&j < n2)		//确定整理的牌段 left-right,只要左牌堆里面还有牌,只要右牌堆里面还有牌
	{
		while (i < n1&&arrL[i] < arrR[j])	//确认左牌堆里面还有牌,并且左牌堆里的这张牌比右牌堆里的那张牌小,把左牌堆这张牌放好
		{
			arr[k] = arrL[i];
			k++;
			i++;
		}
		while (j < n2&&arrL[i] >= arrR[j])	确认右牌堆里面还有牌,并且右牌堆里的这张牌比左牌堆里的那张牌小,把右牌堆这张牌放好
		{
			arr[k] = arrR[j];
			k++;
			j++;
		}
	}
	// 已经放空了一个牌堆,那么剩下的牌只需要按顺序放过去就好了
	while (i < n1)
	{
		arr[k] = arrL[i];
		k++;
		i++;
	}
	while (j < n2)
	{
		arr[k] = arrR[j];
		k++;
		j++;
	}

//#endif

	delete arrL;		//额外空间,自行释放
	delete arrR;
}

template<typename T>
void merge_sort(T arr[], int left, int right)
{
	if (left+1 >= right)	//逾尾的话如果是left》=right 当不是2的n次幂时,会出现 0 0 1, 而 右半部分0 1会不断循环 成 左0 0 右 0 1 ,出现堆栈溢出 
		return;			//确认牌堆里面不止一张牌
	int mid = (right + left) >> 1;	//非逾尾,首先使用就很麻烦,因为 传参为 0,len-1 或者 1,len  多一空间的浪费,把正中间最为一个基准
	merge_sort(arr, left, mid);	//左牌堆太大,再分一分,理一理
	merge_sort(arr, mid, right);//右牌堆太大,再分一分,理一理
	merge(arr, left, mid, right);//左右牌堆已经分好了,进行插入排序
}

归并在放牌的时候进行统计可以得到逆序对的个数(注意等于条件的判断,从小的开始放,当需要放右牌堆里的牌时,这张牌有关的逆序对个数为左牌堆里剩余牌的个数,从大的开始放,当需要放左牌堆里的牌时,有关这张牌的逆序对个数为右牌堆里剩余牌的个数),n1=左牌堆逆序对个数,n2=右牌堆逆序对个数,n3=对左右堆进行排队的逆序对个数,返回 n1+n2+n3

在规模较小的时候使用插入排序,可以提升速度,因为插入排序有更小的常数因子,假设当长度为k时进行插入排序,则一共要进行n/k次长度为k的插入排序,总时间为  n/k*(k^2)  总个数*单个牌堆排序的时间,总耗时为n*k,那么此时总归并排序可以分成两部分,上半部分进行归并,下半部分为插入,长度为k则进行插入,那么归并的深度为  lg (n/k)(可以把k个元素当成一个点,那么就只有n/k个点,n/k就是新的n*,我们对n*进行归并排序,所以深度为lg n* = lg n/k)元素个数没有变,所以归并上半部分耗时  n lg n/k  下半部分耗时 nk  总时长  n lg n/k + nk。

当 n特别大时,比如 k=2,n=1024 ,lg1024=10 》k 可以认为耗时长全在归并上,此时耗时总时长为 n lg n/k。 

若要n lg n/k + nk = n lg n则, lg n/k + k=lg n--》  如果 lg n/k 远大于k 同上,那么 lg n/k< lg n  如果 k稍微大一点,比如n=2^20,k=8    (n lg n/k + nk)/(n lg n)----> (lg n - lg k + k)/(lg n) =1.25 *c (c<1)c是由于插入和归并的指令数量不同引起的,当数据规模比较小时,插入由于总指令比较少而优于归并,(c小于1但也没差多少),我们看前面的1.25,这是一个受数据规模影响的值,2^20 100万左右,挺小的,随便一个题都比这个数据大,当n变大时这个值会趋近于1 可以认为 两者相差不多,如果1.25也在你的接受范围内,那么此时已经算是两者有相同的运行时间

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值