最近在刷算法導論,在第四章看到最大子數組問題,使用C++編碼實現如下。
算法的流程還是使用分治策略,因爲一個數組的最大子數組存在三種情況,以數組的中間位置爲邊界,要麼在左半區間,要麼在右半區間,這兩個情況可以使用遞歸調用,化解爲子問題。第三種情況是左邊界在左區間,右邊界在右區間。這種情況的搜素策略爲從中間位置開始,分別遍歷左區間和右區間,找到累加和最大的左右邊界位置指針。
算法示意圖如下如下:
整個算法計算流程如下:(實際上就是將示意圖轉化爲僞代碼)
其中最關鍵的部分是在中間位置兩側尋找最大子數組的邊界。
算法流程如下:
因爲暴力求解的算法複雜度是,課後練習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;
}