轉載自http://blog.csdn.net/zhanxinhang,作者:花心龜的扮演者ZhanHang
題:求一個矩陣中最大的二維子矩陣(元素和最大).如:
1 2 0 3 4
2 3 4 5 1
1 1 5 3 0
中最大的是:
4 5
5 3
要求:(1)寫出演算法;(2)分析時間複雜度;(3)用C寫出關鍵代碼
分析:方法一、這是最容易想到也是最容易實現的方法。遍曆矩陣(行迭代器為i,列迭代器為j),以當前遍曆到的元素為首a[i,j],計算二維子矩陣的和(sum=a[i,j]+a[i+1,j]+a[i,j+1]+a[i+1,j+1]),並找出和最大的二維矩陣,注意矩陣的最後一行和最後一列不用遍曆。時間複雜度為O(i*j)。
實現代碼:
/** Author:花心龜 Blog:http://blog.csdn.net/zhanxinhang **/ void get_max_22Matrix(int *a,int row,int col,int *result) //a為原矩陣,row,col指a矩陣的行和列,result儲存最終得到的子二維矩陣 { int maxsum=0,result_i,result_j,sum; #define a(i,j) *(a+(i)*col+(j)) //用二維的形式表示一維數組,訪問需要一定的代價 #define result(i,j) *(result+(i)*2+(j)) for(int i=0; i<row-1; i++) for(int j=0; j<col-1; j++) { sum = a(i,j)+a(i+1,j)+a(i,j+1)+a(i+1,j+1); //訪問四個元素並相加得到當前的和 if(maxsum<sum) //更新最大子二維矩陣資料 { maxsum = sum; result_i = i; result_j = j; } } /* 將結果儲存到result二維數組中*/ result(0,0)=a(result_i,result_j); result(1,0)=a(result_i+1,result_j); result(0,1)=a(result_i,result_j+1); result(1,1)=a(result_i+1,result_j+1); #undef a #undef result }
方法二、這是對方法一的改進。分析方法一可知,方法一在每次遍曆中,必須同時訪問四個元素(a[i,j],a[i+1,j],a[i,j+1],a[i+1,j+1]),方法一的遍曆效果(用方框框住的表示當前訪問到或已訪問的元素,元素被框住的次數就越多,表示被訪問的次數也就越多,被染的顏色也就越深)。
可看出,方法一中多個元素被重複訪問多次,要知道訪問一次元素的代價是不容小視的。實際上我們是可以對其進行改進,使每個元素的訪問次數儘可能的降低的。改進方法如下:
一、增加一個變數,last_vsum(叫做“最新縱向和”,v是vertical) 且初始化為last_vsum = a[0,0]+a[1,0],其作用將在下面說明。
二、改變遍曆方式,原先每次訪問四個元素,現在變為每次訪問縱向的兩個元素(a[i,j],a[i+1,j]),橫向遍曆,遍曆的起始點改為第二個元素,終點到最後一個元素。
三、改變求和方式,求和方法是:首先將上一次儲存的和last_vsum加進sum中,再將last_vsum更新為當前縱向的兩個元素a[i,j],a[i+1,j]之和,然後再將last_vsum加入sum中,這樣就得到本次二維矩陣的和可與maxsum進行比較。如此每次求和只需訪問兩個元素a[i,j],a[i+1,j]。
方法二執行步驟與:
實現代碼:
/** Author:花心龜 Blog:http://blog.csdn.net/zhanxinhang **/ void get_max_22Matrix(int *a,int row,int col,int *result) { int maxsum=0,result_i,result_j,sum,last_vsum=0; #define a(i,j) *(a+(i)*col+(j)) #define result(i,j) *(result+(i)*2+(j)) last_vsum = a(0,0)+a(1,0); //初始last_vsum for(int i=0; i<row-1; i++) { for(int j=1; j<col; j++) { sum = last_vsum ; //將last_vsum加入sum last_vsum = a(i,j)+a(i+1,j);//更新last_vsum sum += last_vsum;//將更新後的last_vsum再與sum累加,得到當前子二維矩陣的和 if(maxsum<sum) { maxsum = sum; result_i = i; result_j = j-1; } } } /* 將結果儲存到result二維數組中*/ result(0,0)=a(result_i,result_j); result(1,0)=a(result_i+1,result_j); result(0,1)=a(result_i,result_j+1); result(1,1)=a(result_i+1,result_j+1); #undef a #undef result }
方法二與方法一效果對照圖:
其中,方法二有中間行需要被訪問兩次,總共訪問次數為5+5+2*5=20次。
而方法一3,4,5元素被訪問了4次,總共訪問次數為4+2*8+3*4=32次。
方法二與方法一的時間複雜度相同,但是效率要高於方法一,尤其是在大矩陣情況下效果尤為明顯
方法三、這可作為對方法二的補充。以上方法二中給的樣本矩陣是3*5的矩陣,也就是列大於行的矩陣,但如果是行大於列依然用方法2去執行效果如何呢?效果如所示:
5*3矩陣,訪問次數為6+2*9=24
可見當行大於列時,利用方法2效果已經下降,如果是大矩陣的話可不只只有4的相差啊。
針對這種情況,只需對方法二稍作改正,沒錯,就是原先方法二是橫向遍曆,現在改為縱向遍曆,效果如所示:
同樣是5*3矩陣,訪問次數為5+5+2*5=20
又回到了方法2的訪問次數。
這樣如果是列大於行的矩陣使用方法二,也就是橫向遍曆
如果是行大於列的矩陣使用方法三,也就縱向遍曆
實現代碼略。
…………………………………………
贈一測試代碼:
int main() { int a[3*5]={1,2,0,3,4, 2,3,4,5,1, 1,1,5,3,0}; int result[2*2]={0}; get_max_22Matrix(a,3,5,result); #define result(i,j) *(result+(i)*2+(j)) for(int i =0; i<2; i++) { for(int j=0; j<2; j++) printf("%d ",result(i,j)); printf("\n"); } return 0; }