算法导论第四章
4.1 最大子数组问题
1-1
- 返回数组中最大的元素
1-2
FIND-MAXIMUM-SUBARRAY(A, low, high)
1 Subscript_Low = low
2 Subscript_High = right
3 Subscript_Sum = -∞
4 for i = low to high
5 for j = i to high
6 sum = sum + A[j]
7 if Subscript_Sum < sum
8 Subscript_Sum = sum
9 Subscript_Low = i
10 Subscript_High = j
11 return (Subscript_Low, Subscript_High, Subscript_Sum)
1-3
C++代码
constexpr int CROSSOVER_POINT = 37;
class Data{
private:
int low;
int high;
int sum;
public:
// 构造函数列表初始化
Data(const int &L, const int &H, const int &S) : low(L), high(H), sum(S) { };
// 委托构造函数
Data(void) : Data(0, 0, 0) { };
// 返回sum变量
int Ret_Sum(void) { return sum; };
// 其余的让编译器默认生成就行了
};
/* 暴力算法 */
Data Brute_Force_Approach(int *A, int low, int high)
{
Data Temporary(low, high, A[low]);
for (int i = low; i < high; i++)
{
int sum = 0;
for (int j = i; j < high; j++)
{
sum += A[j];
if (sum > Temporary.Ret_Sum())
Temporary = { i, j, sum };
}
}
return Temporary;
}
/* 分治算法 */
Data FIND_MAX_CROSSING_SUBARRAY(int *A, int low, int mid, int high)
{
int left_sum = A[mid];
int sum = 0;
int max_left = 0;
for (int i = mid; i >= low; i--)
{
sum += A[i];
if (sum >= left_sum)
{
left_sum = sum;
max_left = i;
}
}
int right_sum = A[mid + 1];
sum = 0;
int max_right = 0;
for (int j = mid + 1; j <= high; j++)
{
sum += A[j];
if (sum >= right_sum)
{
right_sum = sum;
max_right = j;
}
}
return { max_left, max_right, left_sum + right_sum };
}
Data Divide_And_Conquer_Solution(int *A, int low, int high)
{
if (high == low)
return { low, high, A[low] };
else
{
int mid = (low + high) / 2;
Data Tamporary_Left = Divide_And_Conquer_Solution(A, low, mid);
Data Tamporary_RIght = Divide_And_Conquer_Solution(A, mid + 1, high);
Data Tamporary_Cross = FIND_MAX_CROSSING_SUBARRAY(A, low, mid, high);
if (Tamporary_Left.Ret_Sum() >= Tamporary_RIght.Ret_Sum() && Tamporary_Left.Ret_Sum() >= Tamporary_Cross.Ret_Sum())
return Tamporary_Left;
else if (Tamporary_RIght.Ret_Sum() >= Tamporary_Left.Ret_Sum() && Tamporary_RIght.Ret_Sum() >= Tamporary_Cross.Ret_Sum())
return Tamporary_RIght;
else
return Tamporary_Cross;
}
}
/* 混合算法(混合暴力算法和分治算法) */
Data find_maximum_subarray_mixed(int *A, int low, int high)
{
if (high <= CROSSOVER_POINT)
Brute_Force_Approach(A, low, high);
else
{
int mid = (low + high) / 2;
Data Tamporary_Left = find_maximum_subarray_mixed(A, low, mid);
Data Tamporary_RIght = find_maximum_subarray_mixed(A, mid + 1, high);
Data Tamporary_Cross = FIND_MAX_CROSSING_SUBARRAY(A, low, mid, high);
if (Tamporary_Left.Ret_Sum() >= Tamporary_RIght.Ret_Sum() && Tamporary_Left.Ret_Sum() >= Tamporary_Cross.Ret_Sum())
return Tamporary_Left;
else if (Tamporary_RIght.Ret_Sum() >= Tamporary_Left.Ret_Sum() && Tamporary_RIght.Ret_Sum() >= Tamporary_Cross.Ret_Sum())
return Tamporary_RIght;
else
return Tamporary_Cross;
}
}
1-4
1-5
参考理解算法导论此题答案后写的
个人理解: 该算法主旨再于,用一个 D a t a Data Data类数组来存储序列 0.. n 0..n 0..n的所有元素的和,然后进行比较,选出最大的。
例如:
D a t a [ 0 ] . s u m Data[0].sum Data[0].sum存储的就是 A [ 0 ] A[0] A[0]的所有元素的和,当然, D a t a [ 0 ] Data[0] Data[0]还会存储下标,这里为 D a t a [ 0 ] . l o w = 0 Data[0].low = 0 Data[0].low=0, D a t a [ 0 ] . h i g h = 0 Data[0].high = 0 Data[0].high=0。
D a t a [ 10 ] . s u m Data[10].sum Data[10].sum存储的就是 A [ 0...10 ] A[0...10] A[0...10]的所有元素的和, D a t a [ 10 ] . l o w = 0 Data[10].low = 0 Data[10].low=0, D a t a [ 10 ] . h i g h = 10 Data[10].high = 10 Data[10].high=10。
如果单单只是这样,那肯定是选不出最大的子数组,所有真正的关键再于
i f ( T a m p o r a r y [ i − 1 ] . R e t _ S u m ( ) < 0 ) if (Tamporary[i - 1].Ret\_Sum() < 0) if(Tamporary[i−1].Ret_Sum()<0)
因为有这条判断语句,所以在我们循环的往 D a t a [ i ] Data[i] Data[i]里面存储数据的时候,会进行对 D a t a [ i − 1 ] Data[i - 1] Data[i−1]的 s u m sum sum数据进行判断是否小于 0 0 0,也就是对前面一个 D a t a Data Data数据元素的 s u m sum sum数据进行判断。
它的意义再于:如果前面的数据加在一起,结果却小于 0 0 0,那么我们不再将前面的和与当前的数据相加,因为前面的数据加起来的结果小于 0 0 0,就算相加,也只是使得当前的数据变小,所以我们直接存储当前的数据与下标。
/* 线性算法 */
Data find_maximum_subarray(int *A, int low, int high) // 传递参数为数组范围下标
{
Data *Tamporary = new Data[high - low + 1];
Tamporary[0] = { low, low, A[low]};
for (int i = 1; i <= high - low; i++)
{
if (Tamporary[i - 1].Ret_Sum() < 0)
Tamporary[i] = { i + low, i + low, A[i + low]};
else
Tamporary[i] = { Tamporary[i - 1].Ret_Low(), i + low, A[i] + Tamporary[i - 1].Ret_Sum() };
}
int sum = Tamporary[0].Ret_Sum();
int number = 0;
for (int i = 1; i <= high - low; i++)
{
if (Tamporary[i].Ret_Sum() >= sum)
{
sum = Tamporary[i].Ret_Sum();
number = i;
}
}
return Tamporary[number];
}
总结:我们构建一个数组 S S S,该数组包含以A的每个索引结尾的最大子数组。也就是说, S [ j ] S[j] S[j]包含以j结尾的最大子数组的信息。我们首先遍历输入以构建 S S S。然后,执行 他们在文字中提出的建议。 这是 n + n = 2 n = Θ ( n ) n + n = 2n =Θ(n) n+n=2n=Θ(n)。