最大子數組問題

最近在刷算法導論,在第四章看到最大子數組問題,使用C++編碼實現如下。

算法的流程還是使用分治策略,因爲一個數組的最大子數組存在三種情況,以數組的中間位置爲邊界,要麼在左半區間,要麼在右半區間,這兩個情況可以使用遞歸調用,化解爲子問題。第三種情況是左邊界在左區間,右邊界在右區間。這種情況的搜素策略爲從中間位置開始,分別遍歷左區間和右區間,找到累加和最大的左右邊界位置指針。

算法示意圖如下如下:
在這裏插入圖片描述
整個算法計算流程如下:(實際上就是將示意圖轉化爲僞代碼)
在這裏插入圖片描述其中最關鍵的部分是在中間位置兩側尋找最大子數組的邊界。
算法流程如下:
在這裏插入圖片描述因爲暴力求解的算法複雜度是n2n^{2},課後練習4-1-2要求寫保利求解代碼如下:

template <typename T>
std::tuple<int, T, int> max_subarray_violence(const std::vector<T> & input, int p, int q)
{
	int left=0, right=0;
	T sum=0;

	for (int i = 0; i < input.size(); i++)
	{
		for (int j = i + 1; j < input.size(); j++)
		{
			T tmp = 0;
			for (int k = i; k <= j; k++)
			{
				tmp += input[k];
			}

			if (tmp > sum)
			{
				left = i;
				right = j;
				sum = tmp;
			}
		}
	}
	return std::make_tuple(left, sum, right);
}

下面是使用分治策略的代碼

template<typename T>
std::tuple<int,T,int> max_subarray(const std::vector<T> &input,int p,int q)
{
	std::tuple<int, T, int> left_result,mid_result,right_result;
	std::tuple<int, T, int> result;
	
	if (q == p)
	{
		return std::make_tuple(p, input[p], q);
	}
	else if(p < q)
	{
		int mid;
		mid = floor((p + q) / 2);
		

		left_result = max_subarray(input, p, mid);
		right_result = max_subarray(input, mid + 1, q);

		mid_result = find_mid_tuple(input,p, mid, q);

		auto left_sum = std::get<1>(left_result);
		auto mid_sum = std::get<1>(mid_result);
		auto right_sum = std::get<1>(right_result);

		if (left_sum >= mid_sum && left_sum >= right_sum)
			result = left_result;
		else if (mid_sum >= left_sum && mid_sum >= right_sum)
			result = mid_result;
		else
			result = right_result;
	}
	return result;
}

上述代碼使用p和q表示上下邊界。
應該注意的是處理特殊情況即只有一個元素的時候p=q時,直接返回元素即可。

計算的核心尋找兩側邊界的代碼如下:

template<typename T>
std::tuple<int, T, int> find_mid_tuple(const std::vector<T> &input,int p, int mid, int q)
{
	T left_sum = input[mid];
	T right_sum = input[mid];
	int left=mid, right=mid;

	T left_tmp=left_sum, right_tmp=right_sum;
	for (int i = 0; i < mid - p; i++)
	{
		left_tmp += input[mid - i - 1];
		if (left_tmp > left_sum)
		{
			left = mid - i - 1;
			left_sum = left_tmp;
		}
	}

	for (int i = 0; i < q - mid; i++)
	{
		right_tmp += input[mid + i + 1];
		if (right_tmp > right_sum)
		{
			right = mid + i + 1;
			right_sum = right_tmp;
		}
	}

	T sum = left_sum + right_sum-input[mid];
	return std::make_tuple (left, sum, right);
}

實際上就是一個順序查找。
測試用例:

void test_maxsubarray()
{
	std::vector<int> v = { 13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7 };
	int n = v.size() - 1;
	std::tuple<int,int,int> result= max_subarray<int>(v, 0, n);
	cout << std::get<0>(result) << endl;
	cout << std::get<1>(result) << endl;
	cout << std::get<2>(result) << endl;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章