第一节课(2)

分而治之

分而治之(D&C)是一种重要的算法设计范例。在分治策略中,我们递归地求解一个问题,在每层递归中应用如下三步骤:

  • 分解(Divide):将问题划分为一些子问题,子问题的形式与原问题一样,只是规模更小
  • 解决(Conquer):递归地求解出子问题。如果子问题规模足够小,则停止递归,直接求解
  • 合并(Combine):将子问题的解组合成原问题的解

分治的核心是合并(Combine/Merge)

例子(Examples):

  • Maximum Contiguous Subarray(最大子数组)
  • Counting Inversions(逆序计数)
  • Integer Multiplication(整数乘法)
  • Ploynomial Multiplication(多项式乘法)
  • QuickSort and Partition(快速排序与划分)
  • Deterministic and Randomized Selection(确定性与随机化选择)

 

Maximum Contiguous Subarray(最大子数组)

正式定义:

  • 输入:一实数数组A[1...n]
  • 子数组A[i...j]的值是:

V(i,j)=\sum_{x=i}^{j}A(x)

找到i <= j,使V(i,j)最大化。

暴力求解

计算每一对i <= j的V(i,j)的值,返回最大值。c++代码实现:

/***************************************************************
	暴力求解(Brute Force):
	计算每一对i <= j的V(i,j)的值,返回最大值。
	复杂度:O(n^3) 
****************************************************************/
double MCS_BF(vector<double> &A)
{
	int sz = A.size();
	if(sz == 0){
		cout << "输入数组为空" << endl;
		return -1;
	}
	double vmax = A[0];
	for(int i= 0; i < sz; ++i){
		for(int j = i; j < sz; ++j){
			double sum = 0;
			for(int x = i; x <= j; ++x){
				sum += A[x];
			}
			if(sum > vmax)
				vmax = sum;
		}
	}
	return vmax;
}

数据再利用

不需要从头计算每一个V(i,j), V(i,j) = V(i,j-1) + A[j]。c++代码实现:

/****************************************************************
	数据再利用(Data_Reuse) :
	不需要从头计算每一个V(i,j), V(i,j) = V(i,j-1) + A[j]
	复杂度:O(n^2)  
*****************************************************************/
double MCS_DR(vector<double> &A)
{
	int sz = A.size();
	if(sz == 0){
		cout << "输入数组为空" << endl;
		return -1;
	}
	double vmax = A[0];
	for(int i= 0; i < sz; ++i){
		double sum = 0;
		for(int j = i; j < sz; ++j){
			sum += A[j];
			if(sum > vmax)
				vmax = sum;
		}
	}
	return vmax;
}

分而治之

设 m = \left \lfloor (low+high)/2 \right \rfloor

图2 子数组可能的位置

数组A[low...high]的最大子数组定是以下三种之一:

  •  S_{1}:位于A[low...m];
  •  S_{2}:位于A[m+1...high];
  • SM:跨越中点

S = max\left \{ S_{1},S_{2},SM \right \}.

c++代码实现:

/****************************************************************
	分而治之(Divide-and-Conquer) 
	复杂度:O(nlogn) 
*****************************************************************/
double FindMaxCrossingSubarr(vector<double> &A, int low, int mid, int high)
{
	double SM1 = A[mid];
	double sum = A[mid];
	for(int i = mid-1; i >= low; --i){
		sum += A[i];
		if(sum > SM1)
			SM1 = sum;
	}
	double SM2 = A[mid + 1];
	sum = A[mid + 1];
	for(int j = mid + 2; j <= high; ++j){
		sum += A[j];
		if(sum > SM2)
			SM2 = sum;
	}
	return SM1+SM2;
}
double FindMaxSubarr(vector<double> &A, int low, int high)
{
	if(low == high)
		return A[low];
	int m = floor((low + high) / 2);
	double S1 = FindMaxSubarr(A,low,m);
	double S2 = FindMaxSubarr(A,m+1,high);
	double SM = FindMaxCrossingSubarr(A,low,m,high);
	return max({S1,S2,SM});
}
double MCS_DC(vector<double> &A)
{
	if(A.empty()){
		cout << "输入数组为空" << endl;
		return -1;
	}
		
	int low = 0;
	int high = A.size() - 1;
	return FindMaxSubarr(A,low,high);
}

运行效果:

图3 运行效果

 

用O(n)时间复杂度解决:Kadane算法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章