1. 程式人生 > 實用技巧 >四種演算法實現最長連續子序列

四種演算法實現最長連續子序列

#include <cstdio>
#include <iostream>
#include <vector>
#include <cmath>
#include <ctime>

using namespace std;

/*
產生長度為length的,元素為[-range,range)中隨機數的vector
*/
vector<int> randomVector(int range, int length) {
	srand(time(0));
	vector<int> v(length);
	for (int i = 0; i < length; i++)
		v[i] = rand() % (range * 2) - range;
	return v;
}
/*
暴力破解 O(n^3)


Sum(i,j) = Sum of seq[i,j]

對於每個i,j,  0 <= i <= j < seq.size()
mcss = max(mcss,Sum(i,j))
*/
int MCSS1(vector<int> seq) {
	int mcss = seq[0];
	int tot = 0;
	for (int i = 0; i < seq.size(); i++) {
		for (int j = i; j < seq.size(); j++) {
			int sum = 0;
			for (int k = i; k <= j; k++) { sum += seq[k]; tot++; }
			mcss = max(mcss, sum);
		}
	}
	cout << "MCSS1,inner loop cnt: " << tot << endl;
	return mcss;
}

/*
O(n^2)演算法

暴力破解演算法有很大的時間上的浪費
考慮已經求出Sum(i,j)的情況下再求Sum(i,j+1)可以直接利用之前的Sum(i,j)的結果加上seq[j+1],而不用重新計算

設S(i) = Sum(0,i),則Sum(i,j) = S(j) - S(i-1)

這裡就是利用了這一特性,記錄序列中每個i( 0 <= i < seq.size )的前i項和,去掉內層迴圈
*/
int MCSS2(vector<int> seq) {
	int tot = 0;
	vector<int> S = { 0 }; 
	for (int i = 0; i < seq.size(); i++) { S.push_back(seq[i] + S[i]); tot++; }

	int mcss = seq[0];
	for (int i = 1; i < S.size(); i++) 
		for (int j = i; j < S.size(); j++) {
			tot++;
			mcss = max(mcss, S[j] - S[i - 1]);
		}
	cout << "MCSS2,inner loop cnt: " << tot << endl;
	return mcss;
}

/*
O(nlogn)演算法


MCSS3(seq,l,r) 返回在seq[l,r)中的最大連續子序列和,那麼MCSS3(seq,0,seq.size)為最終答案

設m為區間中間那個數,seq1為貫穿這個區間的最大連續子序列。

mcss = max(MCSS3(seq,l,m),MCSS3(seq,m,r),seq1)
*/
int MCSS3(vector<int> seq,int l,int r) {
	if (r - l == 1) return seq[l];
	int m = (l + r) / 2;
	int lMax = seq[m - 1], lSum = 0;
	for (int i = m - 1; i >= l; i--) {
		lSum += seq[i];
		lMax = max(lMax, lSum);
	}

	int rMax = seq[m], rSum = 0;
	for (int i = m; i < r; i++) {
		rSum += seq[i];
		rMax = max(rMax, rSum);
	}

	return max(max(MCSS3(seq, l, m), MCSS3(seq, m, r)), lMax + rMax);
}

/*
O(n)演算法 動態規劃

對於seq[i],它可以選擇加入前面的連續子序列,也可以選擇另開一個子序列。

當前面的連續子序列為負數時,選擇另開一個子序列比較好,得到一個包含seq[i]的區域性最大連續子序列
當前面的連續子序列為正數時,加入前面的子序列比較好,同樣得到一個包含seq[i]的區域性最大連續子序列

區域性最大子序列中最大的那個即為答案。
*/
int MCSS4(vector<int> seq) {
	int mcss = seq[0],sum = seq[0];
	for (int i = 1; i < seq.size(); i++) {
		// 如果 sum是負數,選擇另外開啟一個新的序列,因為前面的sum對它無幫助
		if (sum < 0) { sum = 0; }
		sum += seq[i];
		// sum = max(sum+seq[i],seq[i]);
		mcss = max(sum, mcss);
	}
	return mcss;
}

int main() {
	vector<int> a = randomVector(1023, 100);// MCSS = 198

	cout << "MCSS1: " << MCSS1(a) << endl;
	cout << "MCSS2: " << MCSS2(a) << endl;
	cout << "MCSS3: " << MCSS3(a, 0, a.size()) << endl;
	cout << "MCSS4: " << MCSS4(a) << endl;


	return 0;
}