全部每周作业和视频思考题答案和解析 见 浙江大学 数据结构 思考题+每周练习答案汇总
//寻找最大子列 分而治之算法 该算法思想可参见浙江大学陈越老师 数据结构课程
算法要求:假设有一组数列:
a[] = {2,13,2,22,1,-44.2,12,1,-21,..........}
我们目标是找到里面的最大的子列和。首先先定一个函数,参数暂且先不考虑太多,就一个数组a[]和数组的大小n
void findMaxSubSeq(int n,double a[]) {}
接下来思考其功能,
我们要将其分成左右两个部分,然后比较左边最大子列和和右边最大子列和以及跨界最大子列和
假设数组有9个数,坐标从0到8,8/2=4 , 分为两组即0——4 5——8
假设有8个数,坐标从0到7,7/2=3,分为两组即0——3 3——7
而分完以后,还要再分子列的子列,直到每个子列都只有一个元素位置。所以我们在分的时候需要知道该子列的最低坐标值和最大坐标值,然后求出中间值。我们修改一下该函数:
void findMaxSubSeq(int left,int right,double a[]) {}
然后思考一下函数里面的内容。首先先把中间值求出来。首先看左边部分,如果大于中心减左边大于0,则还可以继续划分,否则就返回该值。
int center = (left+right)/2;
if (center-left > 0) {
//还可以继续分
}
else {
//不能再继续分了
}
这样写有点啰嗦,我们仔细想一下,这样判断左右,其实用一句话就能够成功:
if (left == right) {
//说明该区间里就这一个数
//可以返回了
}
int center = (left+right)/2; //left——center center+1——right
由此可知我们的函数返回值不该是void,现在改为int
int findMaxSubSeq(int left,int right,double a[]) {
if (left == right) {
//说明该区间里就这一个数
return a[left];
}
//如果还可以继续分,则执行下面的函数
int center = (left+right)/2; //left——center center+1——right
}
现在开始考虑,我们需要通过该函数,找子列的最大子列,即需要通过递归调用。
那么现在我们如下写:
int leftMax = findMaxSubSeq(left,center,a);
int rightMax = findMaxSubSeq(center+1, right, a);
//然后开始进行跨线扫描
即分别找左右两边的最大子列和,然后再找跨中间的最大子列和,最后再进行比较,然后返回当前子列的最大子列和。
现在思考怎么找跨中间的最大子列和。
我们有左右边界,还有中间值,我们先让:
int sum = a[center] + a[center + 1];
然后从中间先网左边逐步加,找找最大子列和,然后再从中间往右边加,找最大子列和:
//然后开始进行跨线扫描
int sum = a[center] + a[center + 1];
int tempSum = sum;
for (int i = center-1;i>=left;i--) {
tempSum += a[i];
if (tempSum > sum)sum = tempSum;
}
tempSum = sum;
for (int i = center +2;i <= right;i++) {
tempSum += a[i];
if (tempSum > sum)sum = tempSum;
}
这里的sum表示跨线最大子列和。然后定义了一个临时变量tempSum,用来从中间一直加到左右边界上,与sum记录的最大值进行比较。
最后我们将左边最大值和跨线最大值以及右边最大值进行比较:
if (leftMax > sum) {
sum = leftMax;
}
if (rightMax > sum)
sum = rightMax;
return sum;
比较三个数大小的方法多种多样,这里用了比较笨的方法,把最大值赋值给sum,然后最后返回sum。
现在贴出完整函数代码:
//寻找最大子列 分而治之算法
int findMaxSubSeq(int left,int right,int a[]) {
//假设有9个数,8/2=4 , 0——4 5——8
//8个数,7/2=3 0——3 3-7
if (left == right) {
//说明该区间里就这一个数
return a[left];
}
//如果还可以继续分,则执行下面的函数
int center = (left+right)/2; //left——center center+1——right
int leftMax = findMaxSubSeq(left,center,a);
int rightMax = findMaxSubSeq(center+1, right, a);
//然后开始进行跨线扫描
int sum = a[center] + a[center + 1];
int tempSum = sum;
for (int i = center-1;i>=left;i--) {
tempSum += a[i];
if (tempSum > sum)sum = tempSum;
}
tempSum = sum;
for (int i = center +2;i <= right;i++) {
tempSum += a[i];
if (tempSum > sum)sum = tempSum;
}
if (leftMax > sum) {
sum = leftMax;
}
if (rightMax > sum)
sum = rightMax;
return sum;
}
然后我们进行验证:
int b[10] = { 1,2,-3,4,5,6,7,-8,9,-10 };
int xx = findMaxSubSeq(0,9,b);
printf("xx = %d",xx);
最后打印23,是正确的。大家可以再测试一些数组验证正确性。