Skip to content

Commit

Permalink
improving the style of 014
Browse files Browse the repository at this point in the history
  • Loading branch information
pezy committed Apr 9, 2015
1 parent 987518e commit 94b629d
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 47 deletions.
36 changes: 35 additions & 1 deletion 014. Maximum Subarray/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ benefited = max(A[i], benefited + A[i]);
maxv = max(benefited, maxv);
```

聪明的你一定发现了,第一句条恰是**当权者的决策**,第二条恰是**历史学家的总结**。 横批:人类是贪婪的。
聪明的你一定发现了,第一句条恰是**当权者的决策**,第二条恰是**历史学家的总结**。 横批:人类是贪婪的。

所以不要怪任何一方,从他们的立场看,都是最优解。局部最优能否导致整体最优呢? 千年谜题。

Expand All @@ -30,3 +30,37 @@ maxv = max(benefited, maxv);
>If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle
咳咳咳, @Mooophy ,该出来了。。。

-----

@Mooophy 应约而出,给出了[代码](#8),我根据其代码,尝试分析一下分治法的策略:

分治法的灵魂在于一个 **"分"** 字。对于这道题而言,求`A`的最大和子序列,可以给`A`来一刀:

−2,1,−3,4,−1,2,1,−5,4
left | right
crossing

由于子序列连续,它必然属于上述划分中的某一种情况:

- 完全属于 left 部分
- 完全属于 right 部分
- 属于 crossing 部分(即跨越中点)

于是其核心代码如下:

```cpp
int maxSubArray(int A[], int l, int h) {
if (l == h) return A[l]; // 仅有一个数
int m = (l + h) / 2; // 中点位置(划分点)
return std::max({maxSubArray(A, l, m), maxSubArray(A, m+1, h), maxCrossing(A, l, m, h)});
}
```
是否非常的简洁呢?
下面问题集中在 `maxCrossing` 的策略,这个过程可以线性求出。即将其属于 `left` 范畴的 `sum` 与属于 `right` 范畴的 `sum` 加起来即可。
实现代码请见:[divide_conquer.h](divide_conquer.h)
这部分的详细思路可参考 CLRS 的第四章分治法的第一小节。
27 changes: 27 additions & 0 deletions 014. Maximum Subarray/divide_conquer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include <algorithm>
#include <climits>

class Solution {
int maxCrossing(int A[], int l, int m, int h) {
int left_max = INT_MIN, right_max = INT_MIN;
for (int i=m, sum=0; i >= l; --i) {
sum += A[i];
if (left_max < sum) left_max = sum;
}
for (int i=m+1, sum=0; i <= h; ++i) {
sum += A[i];
if (right_max < sum) right_max = sum;
}
return left_max + right_max;
}

int maxSubArray(int A[], int l, int h) {
if (l == h) return A[l];
int m = (l + h) / 2;
return std::max({maxSubArray(A, l, m), maxSubArray(A, m+1, h), maxCrossing(A, l, m, h)});
}
public:
int maxSubArray(int A[], int n) {
return maxSubArray(A, 0, n-1);
}
};
8 changes: 3 additions & 5 deletions 014. Maximum Subarray/main.cc
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
#include <iostream>
#include "solution.h"
//#include "divide_conquer.h"

int main()
{
int A[] = {-2,1,-3,4,-1,2,1,-5,4};
int A[] = {-2,1,-3,4,-1,2,1,-5,4};
Solution s;
std::cout << s.maxSubArray(A, 9) << std::endl;

SolutionByDivideAndConquer dac;
std::cout << s.maxSubArray(A, 9) << std::endl;

return 0;
}
}
41 changes: 0 additions & 41 deletions 014. Maximum Subarray/solution.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,44 +12,3 @@ class Solution {
return maxv;
}
};

class SolutionByDivideAndConquer{
//O(n)
int find_max_crossing_subarray(int arr[], unsigned low, unsigned mid, unsigned hgh)
{
auto accumulation = 0;

auto lft_sum = accumulation = arr[mid];
if (mid > low)
for (auto i = mid - 1; i != low - 1; --i)
if ((accumulation += arr[i]) > lft_sum)
lft_sum = accumulation;

auto rht_sum = accumulation = arr[mid + 1];
if (hgh > mid)
for (auto i = mid + 2; i != hgh + 1; ++i)
if ((accumulation += arr[i]) > rht_sum)
rht_sum = accumulation;

return lft_sum + rht_sum;
}

//O(n lg n)
int divide_and_conquer(int arr[], unsigned low, unsigned hgh)
{
if (low == hgh) return arr[low];

auto mid = (low + hgh) / 2;
auto lft = divide_and_conquer(arr, low, mid);
auto rht = divide_and_conquer(arr, mid + 1, hgh);
auto crs = find_max_crossing_subarray(arr, low, mid, hgh);

return std::max({ lft, rht, crs });
}

public:
int maxSubArray(int A[], int n)
{
return divide_and_conquer(A, 0, n - 1);
}
};

0 comments on commit 94b629d

Please sign in to comment.