算法分治复习

2.6棋盘覆盖

1、基本思想

当k>0时,可以将2^k * 2k的棋盘分割成4个2(k-1) * 2^(k-1)子棋盘。因为特殊格子肯定位于4个较小的子棋盘中,其余3个格子中没有特殊方格。为了将这3个格子变成特殊棋盘,可以用一个L型骨牌覆盖这3个较小棋盘的汇合处。从而将原问题转换为4个相同的子问题。递归地使用这种分割,直到棋盘简化为1x1的棋盘。
在这里插入图片描述

2、代码

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

2.7合并排序

1、基本思想

将待排序元素分成大小大致相同的两个子集合,分别对两个子集合进行排序,最终将排好序的子集合合并成所要求的排好序的集合。

2、算法步骤

① 分解:将待排的元素数组一分为二,直到子序列中只有一个元素。
② 求解:只有一个元素的序列就是有序序列。
③ 合并:将两两子序列合并,并按照增序或降序统一排序,直到得到原序列的有序序列。

3、举例

在这里插入图片描述

4、伪代码

在这里插入图片描述

5、复杂度

可以用主定理2求解
在这里插入图片描述
在这里插入图片描述

2.8快速排序

1、基本思想

在这里插入图片描述

2、算法步骤

1)分解:对于输入的子数组a[p:r],以a[p]为基准元素将a[p:r]划分为3段a[p:q-1],a[q],a[q+1,r]使a[p:q-1]中任何一个元素小于等于a[q],而a[q+1,r]中任何一个元素大于等于a[q]。下标q在划分过程中确定。

2)递归求解:通过递归调用快速排序算法分别对a[p:q-1]和a[q+1:r]进行排序。

3)合并:对于a[p:q-1]和a[q+1:r]的排序是就地进行的,所以在a[p:q-1]和a[q+1:r]排好序后,不需要进行任何计算,就已经排好序了。

3、举例

仅一轮,以第一个元素为基准元素。
在这里插入图片描述

4、代码

// An highlighted block
int findprivot(int array[],int begin,int end){
	return (begin+end)/2;    //取中间值为轴值 
}

bool prior(int pivot,int &elem){
	return pivot<elem; //增序 
}

void swap(int array[],int i,int j){
	int temp=array[j];
	array[j]=array[i];
	array[i]=temp;
}
 
int partition(int array[],int l,int r,int& pivot){
	do{
		while(prior(array[++l],pivot));
		while((l<r)&&prior(pivot,array[--r])); 
		swap(array,l,r);
	} while(l<r);
	return l;
}

void qsort(int array[],int begin,int end){
	if(end<=begin)return ;
	int pivotindex = findprivot(array,begin,end);//轴值 
	swap(array,pivotindex,end);//把轴值放到数组的最后一个元素 
	int k=partition(array,begin-1,end,array[end]);//划分 
	swap(array,k,end);//把轴值放到正确位置 
	qsort(array,begin,k-1);
	qsort(array,k+1,end);
}

5、复杂度

在这里插入图片描述

2.9线性时间选择

一、选择最大值和最小值

1、基本思想

① 分解:将原问题划分为规模较小的子问题,直到子问题中只有一个元素。

② 求解:只有一个子问题的元素既是子问题的最大值,也是子问题的最小值。

③ 合并:将子问题的解合并为原问题的解,两两合并,最小值取子问题的最小值中的最小, 最大值取子问题的最大值中的最大。直到得到原问题的最大最小值。

2、伪代码

在这里插入图片描述

3、复杂度分析

因为算法的比较大小值是常数级操作,将原问题划分为左右两个子问题,所以时间复杂度递归表达式为:
当n=1时,T(n)=O(1);
当n>1时,T(n)=2*T(n/2)+O(1);
所以,分治算法的时间复杂度为O(logn)。

二、查找第k小的值

1、算法思想

找到划分的基准
1、将n个输入元素划分成[n/5]个组。
2、用任意一种排序算法,将每个组中的元素排序,并取出每组的中位数,共[n/5]个。
3、递归调用select找到这[n/5]个元素的中位数。如果[n/5]是偶数,就找它的中位数中较大的一个。以这个元素作为划分的基准。

2、举例

在这里插入图片描述

4、复杂度

在这里插入图片描述

3、代码

在这里插入图片描述

// An highlighted block
void select(int* a,int p,int r,int k)
{
	if (r-p<75)     // 旧方法
	{
		//用某个简单排序算法对数组a[p:r]进行排序
		//return a[p+k-1];
		return RandSelect(a, p, r, k); //排序并返回排序后的中位数a[p+k-1]
	}
	else     
	{
		// 新方法
		for ( int i = 0; i<(r-p)/5; i++ ){     
			int s=p+5*i; // 段i 的起点
			int t=s+4; // 段i 的终点
			Sortysh(a,s,t); // 对本段中5 个元素排序
			int tmp=a[s+2];a[s+2]=a[p+i];a[p+i]=tmp;   // 第3 小到p+i 处
		}     
 // 各段的中位数已经移到了a[p],a[p+1],…a[p+ 段数(r-p)/5]
// 用同样的算法找中位数的中位数
		int x =select(p, p+(r-p)/5, (r-p)/5/2);     //x 是中位数的中位数
		int i=partition(p,r,x)// 按此进行普通划分 ,为0.75 原<0.9
		j=i-p+1; // 搜索速度很快  结束条件
		if (k<j) {return select(p,i,k);} 
		else if (k==j) { return a[i];}
		else {return select(i+1,r,k-j);} 
	}
}

2.11循环赛日程表

1、基本思想

按照分治策略,可以将所有选手对分成两半,n个选手的比赛日程可以通过为n/2个选手设计的比赛日程表来决定。递归地用这种一分为二的策略对选手进行分割,直到只剩下两个选手时,就只要让这两个选手进行比赛了。

2、表格

n行n列的表格,第i选手在第j-1天遇到的选手。
填充原则:对角线填充
在这里插入图片描述
在这里插入图片描述

3、代码

// An highlighted block
for(int i=m+1;i<=2*m;i++) { //i控制行
	for(int j=m+1;j<=2*m;j++) //j控制列
	{
		a[i][j+(t-1)*m*2]= a[i-m][j+(t-1)*m*2-m];
		/*右下角的值等于左上角的值 */
		a[i][j+(t-1)*m*2-m] =a[i-m][j+(t-1)*m*2];
		/*左下角的值等于右上角的值 */
	}
}

集合划分问题–引申:数组中位数O(n)时间复杂度求法

题目

设S 是n (n 为偶数)个不等的正整数的集合,要求将集合S 划分为子集S1 和S2 ,使得| S1|=| S2|=n/2 ,且两个子集元素之和的差达到最大。

基本思想

把前n/2小的元素尽可能放到S1中,剩下的元素放到s2中。可以采用类似快排的方法,只要找到了中位数将比中位数小的放到左边,把中位数大的放到右边就可以了。

利用快速排序的划分思想,设正整数集合为数组S,划分为前半个数组S1,后半个数组为S2,若第一次划分的轴值是中位数,则返回;若不是继续划分中位数所在的部分。

代码

链接: link

复杂度

类似于线性时间求解中位数,时间复杂度也为O(n)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值