左神算法基础class1——例子7,8归并排序,小和问题,逆序对问题

左神算法基础class1——例子7,8归并排序,小和问题,逆序对问题

  • 题目:归并排序的细节讲解与复杂度分析
    • 分析
    • 完整代码
    • 复杂度
  • 应用:小和问题
    • 分析
    • 核心代码
    • 完整代码
  • 应用:逆序对问题
    • 分析
    • 核心代码
    • 完整代码

题目:归并排序的细节讲解与复杂度分析

分析

整体思路先左边从小到大排序,再右边从小到大排序,最后整体排序。首先把数字分为两部分[l,m]和[m+1,r],所以主函数调用时l=0,r=length-1
左神算法基础class1——例子7,8归并排序,小和问题,逆序对问题_第1张图片
代码分为两部分:
1.排序:不断调用自己,停止条件是l==r左指针等于右指针

void mergesort(int l,int r,int b[])
{
     
	if(l == r)
		return;
	int m = (r+l)/2;//l+(r-l)>>1
	mergesort(l,m,b);
	mergesort(m+1,r,b);
	merge(l,m,r,b);
}

2.合并
设置两个指针分别指向左侧第一个数和,右侧第一个数。当while(p1<=m &&p2<=r)时,可以继续从小到大排序。后面两个while指若一个排完,另一个还未排完,直接把未排完的数加在后面。

void merge(int l,int m,int r,int b[])
{
     
	int help[B_MAX];
	int i = 0;
	int p1 = l;
	int p2 = m + 1;
	while(p1<=m &&p2<=r)
	{
     
		help[i++] = (b[p1]>b[p2]? b[p2++]:b[p1++]);
	}
	while(p1<=m)
	{
     
		help[i++] = b[p1++];
	}
	while(p2<=r)
	{
     
		help[i++] = b[p2++] ;
	}
	for(i = 0; i < r-l+1; i++){
     
		b[l+i] = help[i];
	}
}

完整代码

#include 
#include
#define B_MAX 7
using namespace std;

void merge(int l,int m,int r,int b[])
{
     
	int help[B_MAX];
	int i = 0;
	int p1 = l;
	int p2 = m + 1;
	while(p1<=m &&p2<=r)
	{
     
		help[i++] = (b[p1]>b[p2]? b[p2++]:b[p1++]);
	}
	while(p1<=m)
	{
     
		help[i++] = b[p1++];
	}
	while(p2<=r)
	{
     
		help[i++] = b[p2++] ;
	}
	for(i = 0; i < r-l+1; i++){
     
		b[l+i] = help[i];
	}
}


void mergesort(int l,int r,int b[])
{
     
	if(l == r)
		return;
	int m = (r+l)/2;
	mergesort(l,m,b);
	mergesort(m+1,r,b);
	merge(l,m,r,b);
}

int main()
{
     
	srand((unsigned)time(NULL));
	int b[B_MAX];//={1,8,5,2,7,4,3};
	//cout<<"b=  ";
	for(int i = 0;i < B_MAX;i++)
	{
     
		b[i] = rand()%10;
		//cout<
	}
	//cout<

	mergesort(0,B_MAX-1,b);



	system("pause");
	return 0;
}

复杂度

时间复杂度O(N*logN),额外空间复杂度O(N)

递归行为的复杂度计算公式:T(N) = a*T(N/b) + O(N^d)

  1. log(b,a) > d -> 复杂度为O(N^log(b,a))
  2. log(b,a) = d -> 复杂度为O(N^d * logN)
  3. log(b,a) < d -> 复杂度为O(N^d)
    公式解释:T(N)指样本大小为N时的时间复杂度,a指子过程调用次数,b指子过程的样本,d指除去递归外的代价。
    本题:子过程分为左和右共两次a=2,子过程样本规模为二分之一b=2。额外代价是对N个数遍历一遍排序d=1。故时间复杂度为O(N*logN)。

应用:小和问题

在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。
例子:[1,3,4,2,5]
1左边比1小的数,没有;
3左边比3小的数,1;
4左边比4小的数,1、3;
2左边比2小的数,1;
5左边比5小的数,1、3、4、2;
所以小和为1+1+3+1+1+3+4+2=16

分析

在归并过程中计算小和,实质是如果左数小于右数,则记录
1 3 4|2 5
/
1 3 |4 2|5
/
1|3
1.在1|3归并中,1<3,产生小和1;
2.在1 3|4归并中,p1指向1,p2指向4。1<4,记录小和1,p1指向3,3<4,继续记录小和3;
3.右侧2|5,2<5,记录小和2;
4.1 3 4 | 2 5中,p1指向1,p2指向2。1<2,则2右侧大于2的数都构成小和,记录小和2个1;
p1移到3处,3>2不记录,p2右移到5,3<5,由于5右侧没有数了,所以记录小和1个3;p1继续右移,4<5,再继续记录小和1个4;
5.小和 = 1+1+3+2+21+13+1*4 = 16

核心代码

result负责记录当前小和,当左侧小时,把右侧当前到右侧末尾的个数乘以左侧较小的数得到小和。

while(p1<=m&&p2<=r)
	{
     
		result += b[p1]<b[p2] ? ((r-p2+1)*b[p1]):0;
		help[i++] = b[p1]>b[p2] ? b[p2++]:b[p1++];
	}

完整代码

#include

#define B_Length 5
using namespace std;
int merge(int l,int m,int r,int b[])
{
     
	int help[B_Length];
	int p1 = l;
	int p2 = m+1;
	int i = 0;
	int result = 0;
	while(p1<=m&&p2<=r)
	{
     
		result += b[p1]<b[p2] ? ((r-p2+1)*b[p1]):0;
		help[i++] = b[p1]>b[p2] ? b[p2++]:b[p1++];
	}
	while(p1<=m)
	{
     
		help[i++] = b[p1++];
	}
	while(p2<=r)
	{
     
		help[i++] = b[p2++];
	}
	for (i = 0; i < r-l+1; i++) 
	{
     
		b[i+l] = help[i];
	}
	return result;
}

int mergesort(int l,int r,int b[])
{
     
	if(l == r)
		return 0;
	int m = l+((r-l)>>1);
	
	return mergesort(l,m,b)+mergesort(m+1,r,b)+merge(l,m,r,b);
}

int main()
{
     
	int b[B_Length]={
     1,3,4,2,5};
	int a = 0;
	a = mergesort(0,B_Length-1,b);

	system("pause");
	return 0;
}

应用:逆序对问题

在一个数组中,左边的数如果比右边的数大,则折两个数构成一个逆序对,请打印逆序对个数。

分析

逆序对问题的实质是记录左比右大的次数。与小和问题相比,只需要记录左侧较大数当前位置到左侧最终位置的个数即可。

核心代码

while(p1<=m&&p2<=r)
	{
     
		result += b[p1]>b[p2] ? (m-p1+1):0;
		help[i++] = b[p1]>b[p2] ? b[p2++]:b[p1++];
	}

完整代码

#include

#define B_Length 5
using namespace std;
int merge(int l,int m,int r,int b[])
{
     
	int help[B_Length];
	int p1 = l;
	int p2 = m+1;
	int i = 0;
	int result = 0;
	while(p1<=m&&p2<=r)
	{
     
		result += b[p1]>b[p2] ? (m-p1+1):0;
		help[i++] = b[p1]>b[p2] ? b[p2++]:b[p1++];
	}
	while(p1<=m)
	{
     
		help[i++] = b[p1++];
	}
	while(p2<=r)
	{
     
		help[i++] = b[p2++];
	}
	for (i = 0; i < r-l+1; i++) 
	{
     
		b[i+l] = help[i];
	}
	return result;
}

int mergesort(int l,int r,int b[])
{
     
	if(l == r)
		return 0;
	int m = l+((r-l)>>1);
	
	return mergesort(l,m,b)+mergesort(m+1,r,b)+merge(l,m,r,b);
}

int main()
{
     
	int b[B_Length]={
     2,5,1,3,4};
	int a = 0;
	a = mergesort(0,B_Length-1,b);

	system("pause");
	return 0;
}

你可能感兴趣的:(左神算法基础课,归并,小和问题,逆序对问题,c++)