1. 程式人生 > >求輸入序列的最大子序列

求輸入序列的最大子序列

問題:給出一個有限長度的實數序列,求出其子序列中和為最大的子序列值?

方法1:設定兩個迴圈遍歷序列,分別作為子序列的左右兩端,再加一個迴圈將子序列中所有項相加,最後演算法的時間複雜度為O(N^{3})

void maxSubList(int list[],int length) {
	int maxSublist = 0,l=0,r=0;
	int thisSubList = 0;
	int i ,j,k;
	for (i = 0; i < length; i++) {		//定義序列左端
		
		for (j = i; j < length; j++) {	//定義序列右端
			for (k = i; k < j; k++) {
				thisSubList += list[j];		//將序列從左端i加到右端j
			}
			
			if (thisSubList > maxSublist)
			{
				maxSublist = thisSubList;		//更新最大序列
				l = i;							//記錄最大子序列的左右下標
				r = j;
			}
		}
	}
	cout << maxSublist << endl;
	cout << l<<","<<r << endl;
}

方法2:首先思考,演算法需要做的是遍歷所有的子序列,在遍歷過程中就可以將所有的子序列進行求和比較,然後更新最大子序列即可,所以第三個迴圈可以省去。最後演算法的時間複雜度為O(N^{2})

void maxSubList(int list[],int length) {
	int maxSublist = 0,l=0,r=0;
	int thisSubList = 0;
	int i ,j;
	for (i = 0; i < length; i++) {		//定義序列左端
		thisSubList = 0;
		for (j = i; j < length; j++) {	//定義序列右端
		
				thisSubList += list[j];		//將序列從左端i加到右端j
			
			if (thisSubList > maxSublist)
			{
				maxSublist = thisSubList;		//更新最大序列
				l = i;							//記錄最大子序列的左右下標
				r = j;
			}
		}
	}
	cout << maxSublist << endl;
	cout << l<<","<<r << endl;
}

方法3:根據分治的思想,對序列進行劃分。最大子序列可以在左邊子式,可以在右邊子式,也可以是跨越中間的子式,然後將左右子式依次劃分,求子式中的最大子式。如圖所示:

int MaxSubList(int numL, int numR,int* list) {
	if (numL == numR) {

		return list[numL];				//定義最終的返回值
	}
	int l = 0, r = 0;
	int numMid = (numL+numR)/2;
	int lMax = MaxSubList(numL, numMid, list);//回撥求左邊的最大子式
	int rMax = MaxSubList(numMid + 1, numR, list);//回撥求右邊的最大子式
	int midMax=0,sumL=list[numMid],sumR=list[numMid+1],sum=0;  
	for (int i = numMid; i >=numL; i--) {
		sum += list[i];									
		if (sum > sumL)										//計算中間部分的最大子式
			sumL = sum;
	}								
	sum = 0;
	for (int j = numMid + 1; j <= numR; j++) {
		sum += list[j];
		if (sum > sumR)
			sumR = sum;
	}

	midMax = sumL + sumR;
	if (lMax > midMax)
	{
		midMax = lMax;								//判斷最後的大小

	}
	if (rMax > midMax)
	{
		midMax = rMax;

	}


	return  midMax; 
}

最後演算法的時間複雜度為O(NlogN)

4、最後介紹一下線上處理演算法。線上處理的意思就是邊讀入資料邊進行判斷整理,在逐項累加的過程中判斷累加數列是否為負,若為負數,則加後面的項只會使後面的數變小,所以清空累加數列。然後判斷累加後的數列與之前記錄的最大數列之間的大小,最後得出結果。它的演算法複雜度為O\left ( N\right )

int MaxSubList(int* list,int length) {
	int thisSum = 0, maxSum = 0,startNum=0,stopNum=0;
	for (int i = 0; i < length; i++) {
		thisSum += list[i];					//逐項累加
		if (thisSum < 0) {
			thisSum = 0;					//判斷累加後的數列若為負數,則捨棄該累加數列
			startNum = i + 1;				//同時將子數列起始序號重置
		}
		if (thisSum > maxSum) {
			maxSum = thisSum;				//判斷累加後的數列若大於之前記錄的最大子數列,則對最大子數列進行更新
			stopNum = i;					//同時將子數列的終止序號重置
		}
	}
	cout << startNum << "," << stopNum << endl;
	return maxSum;

該演算法不僅時間複雜度最低,而且還能同時求出最大子數列的起始,是目前最優的演算法。