在之前的第三章中,主要討論了標準漸進符號,讓我們對算法的複雜度有了一個形式上的認識,這一章中討論分治策略,原諒我大一線性代數現在全部都忘記了,所以這裏我就不討論strassen算法了(應該以後會把這個地方補充起來)。
這一章,作者使用了一個求最大子數組的例子來說明分治策略。分治策略我個人認爲它的精髓就是,把大的問題化成較小的問題,當問題足夠小的時候,也就成爲了基本問題的時候就可以直接求解。顯然對數組的任何一個部分求最大子數組,都可以看成對本來數組求最大子數組的子問題。所以,這裏就可以對子數組使用分治策略,當數組中只有一個元素的時候,顯然這個問題可以直接解決,我們也就找到遞歸算法中的出口。
我們再思考,如果把數組分成兩個部分,那麼最大數組的存在只有三種情況。第一種情況是在分成兩個子數組的地一個子數組中,第二種情況是在分成兩個的子數組的另一個子數組當中,第三種情況是穿越兩個子數組形成一個最大子數組。理解了這個情況我們就可以很容易地理解代碼了,下面附上我自己實現的c語言源代碼。(僅供參考)
<span style="font-size:18px;">typedef struct log_max_subarray {
int sum;
int left;
int right;
}LMS;//定義一個結構體,用來記錄最大子數組的最左邊的下標,最右邊的下標,以及最大值。</span>
<span style="font-size:18px;">//這個算法的作用是用來找出穿越兩個子數組存在的最大子數組
LMS *cross_max_subarray (EleType *arr , int start , int mid , int end) {
int i;
int temp = 0;
int tempsum = INT_MIN;
int sumright = 0;
int sumleft = 0;
LMS *res = (LMS *) malloc (sizeof (LMS));
//先查找左邊的下標
for (i = mid ; i >= start ; i--) {
temp += arr[i];
if (temp > tempsum) {
tempsum = temp;
res->left = i;
}
}
temp = 0;
sumleft = tempsum;
tempsum = INT_MIN;
//再查找右邊的下標
for (i = mid + 1 ; i <= end ; i++) {
temp += arr[i];
if (temp > tempsum) {
tempsum = temp;
res->right = i;
}
}
sumright = tempsum;
res->sum = sumright + sumleft;
return res;
}
LMS *find_max_subarray (EleType *arr , int start , int end) {
int mid = (start + end) / 2;
LMS *res = (LMS *) malloc (sizeof (LMS));
LMS *res1 = (LMS *) malloc (sizeof (LMS));
LMS *res2 = (LMS *) malloc (sizeof (LMS));
LMS *res3 = (LMS *) malloc (sizeof (LMS));
if (start == end) {
res->left = start;
res->right = end;
res->sum = arr[start];
return res;
}else {//下面三行代碼對應於之前所說的三種子數組存在的情況,每種情況對應於一行代碼
res1 = find_max_subarray (arr , start , mid);
res2 = find_max_subarray (arr , mid + 1 , end);
res3 = cross_max_subarray (arr , start , mid , end);
if ((res1->sum >= res2->sum) && (res1->sum >= res3->sum))
return res1;
else if ((res2->sum >= res1->sum) && (res2->sum >= res3->sum))
return res2;
else
return res3;
}
}
</span>
算法的分析:既然是分治算法,那麼我可以用遞歸表達試來刻畫這個算法的時間複雜度,我們可以看見這個算法將問題分成兩個子問題,而每個問題的規模是原問題的二分之一,而我們可以看到<span style="font-size:18px;">LMS *cross_max_subarray (EleType *arr , int start , int mid , int end)這個函數的時間複雜度是O(n)所以我們可以直接寫出這個遞歸表達式如下<img src="https://img-blog.csdn.net/20160821143406543?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /></span>
根據之前第二章討論的問題我們可以得知這個算法的時間複雜度是O(n*lgn),當然我們之後還會學會主方法。
書上還提到了線性的最大子數組的算法。這裏好像有兩種寫法,我都附上,其中一個以前在做題目的時候自己竟然碰巧寫出來過。
第一種算法
int find_max_subarray (EleType *arr , int start , int end) {
int maxending = arr[start];
int maxsofar = arr[start];
int i;
for (i = start + 1 ; i <= end ; i++) {
if (maxending < 0)
maxending = arr[i];
else if (maxending >= 0)
maxending += arr[i];
maxsofar = (maxsofar > maxending ? maxsofar : maxending);
}
return maxsofar;
}
第二種算法:(嚴格按照書上的思路來實現的)
int find_max_array (EleType *arr , int start , int end) {
LMS *res = (LMS *) malloc (sizeof (LMS));
EleType arr_sum[end - start + 1];
EleType temp = 0;
EleType i;
EleType max_statoi = arr[start];
EleType min = arr[start];
for (i = start ; i <= end ; i++) {
temp += arr[i];
arr_sum[i-start] = temp;
}
EleType max_itotail = arr_sum[0];
for (i = start ; i <= end-1 ; i++) {
if (min >= arr_sum[i-start])
min = arr_sum[i-start];
if (min <= 0)
max_itotail = arr_sum[i-start+1] - min;
else if (min > 0)
max_itotail = arr_sum[i-start+1];
max_statoi = (max_statoi > max_itotail ? max_statoi : max_itotail);
}
return max_statoi;
}