第四章:多維陣列和矩陣 ------------- 4.4 找出邊界為1的最大子方陣
找出邊界為1的最大子方陣:
1、未優化的題解:(複雜度較高)
//虛擬碼
max(A,N){
int n=N;
while(n>0){
for(i from 0 to N-1){
if(i+n>N) break;
l3:
for(j from 0 to N-1){
if(j+n>N) break;
//i,j就是頂點
r=i;
c=j;
//上面的邊
while(c<j+n){
if(A[r][c]==0) continue l3;
c++;
}
c--;
//右邊的邊
while(r<i+n){
if(A[r][c]==0) continue l3;
r++;
}
r--;
//下邊的邊
while(c>=j){
if(A[r][c]==0) continue l3;
c--;
}
c++;
//左邊的邊
while(r>=i){
if(A[r][c]==0) continue l3;
r--;
}
//r++;--------- 無需恢復
return n;
}
}
n--;
}
return 0;
}
程式碼:
#include<iostream>
#include<vector>
using namespace std;
int solve(vector<vector<int> > A){
int N=A.size();
int n=N;
while(n>0)
{
for(int i=0;i<N;i++)
{
if(i+n>N) break;
//第二層迴圈:
int j=0;
l3:
while(j++<N){
if(j+n>N) break;
//檢查四條邊
//i,j就是頂點
int r=i,c=j;
//上面的邊
while(c<j+n)
{
if(A[r][c++]==0) goto l3;
}
c--;
//右邊的邊
while(r<i+n)
{
if(A[r++][c]==0) goto l3;
}
r--;
//下邊的邊
while(c>=j)
{
if(A[r][c--]==0) goto l3;
}
c++;
//左邊的邊
while(r>=i)
{
if(A[r--][c]==0) goto l3;
}
//r++;--------- 無需恢復
return n;
}
}
n--;
}
return 0;
}
int main()
{
vector<vector<int> > arr;
int row=5,col=5;
//二維向量賦初值
int i=0,j=0;
//賦初值
vector<int> temp;
//第一行
temp.push_back(0);
temp.push_back(1);
temp.push_back(1);
temp.push_back(1);
temp.push_back(1);
arr.push_back(temp);
temp.clear();
//第二行
temp.push_back(0);
temp.push_back(1);
temp.push_back(0);
temp.push_back(1);
temp.push_back(0);
arr.push_back(temp);
temp.clear();
//第三行
temp.push_back(0);
temp.push_back(1);
temp.push_back(1);
temp.push_back(1);
temp.push_back(1);
arr.push_back(temp);
temp.clear();
//第四行
temp.push_back(0);
temp.push_back(1);
temp.push_back(1);
temp.push_back(1);
temp.push_back(1);
arr.push_back(temp);
temp.clear();
//第五行
temp.push_back(0);
temp.push_back(1);
temp.push_back(0);
temp.push_back(1);
temp.push_back(1);
arr.push_back(temp);
temp.clear();
//列印二維向量
for(int i=0;i<row;i++)
{
for(int j=0;j<col;j++)
{
cout<<arr[i][j]<<" ";
}
cout<<endl;
}
cout<<endl;
cout<<solve(arr);
return 0;
}
continue:結束本次迴圈,不執行迴圈體內continue下面的語句,繼續下次迴圈。
break:終止整個迴圈體,無論多少層。
goto:與標誌聯用,容易造成程式的混亂。
結果:
2、邊界為1的最大子方陣優化:
①、可以通過對原有矩陣進行預處理的方式,這個思路類似於動態規劃中的打表法。
②、我們可以構建一個三維陣列,二維平面空間和矩陣大小相同,第三維儲存兩個元素,第一個元素儲存包括自己以及自
己右方連續的1的個數,如果自己為0,那麼自己就儲存為0。第二個元素儲存包括自己以及自己下方連續的1的個數,如果
自己為0,那麼自己也儲存為0。
③、通過生成這個輔助表,就可以在判斷是否是最大方陣的時候加快判斷速度,可以將4個while迴圈去除,而通過這個輔助
表只需判斷一次即可,時間複雜度為O(1),那麼整個演算法的時間複雜度就為N*N*N了,為O(N3)。
假如矩陣為:
-----------------------------
{ 1, 1, 0, 1 }
{ 1, 1, 1, 1 }
{ 1, 1, 0, 1 }
{ 1, 1, 1, 1 }
-----------------------------
生成的輔助陣列為:
-----------------------------
2,4 1,4 0,0 1,4
4,3 3,3 2,1 1,3
2,2 1,2 0,0 1,2
4,1 3,1 2,1 1,1
-----------------------------
程式碼:
public class MaxSquare_1 {
public static void main(String[] args) {
// int[][] A = {
// { 0, 1, 1, 1, 1 },
// { 0, 1, 0, 1, 0 },
// { 0, 1, 1, 1, 1 },
// { 0, 1, 1, 1, 1 },
// { 0, 1, 0, 1, 1 }
// };
int [][]A = new int[][] {
{ 1, 1, 0, 1 },
{ 1, 1, 1, 1 },
{ 1, 1, 0, 1 },
{ 1, 1, 1, 1 },
};
generateHelpRec(A);
System.out.println("生成的輔助陣列為:");
System.out.println("=============================");
print(rec, A.length);
System.out.println("=============================");
System.out.println("最大子方陣的邊長為:"+solve(A));
}
private static void print(int[][][] rec, int N) {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(rec[i][j][0] + "," + rec[i][j][1] + "\t");
}
System.out.println();
}
}
static int [][][]rec;
/**
* 記錄每個元素往右和往下有多少個連續的1
* @param A
*/
private static void generateHelpRec(int[][] A) {
int N = A.length;
rec = new int[N][N][2];
int row = N-1;
// 初始化最後一行,因為記錄的是往右和往下的,所以必須從最後一行向上初始化,而且從右向左初始化
for(int j=N-1;j>=0;j--){
int value = A[row][j];
if (value==1) {
if (j==N-1) { // 避免陣列越界
rec[row][j][0] = 1; // 右邊界
}else {
// A的元素值為1,rec在這個位置的連續1的個數 = 右邊位置的連續的1的個數+1
rec[row][j][0] = rec[row][j+1][0] + 1;
}
// 最後一行的下方的1的連續數==1
rec[row][j][1] = 1;
}
}
row--; //開始初始化倒數第二行到第一行
for(int i = row;i>=0;i--){
for (int j = N-1; j>=0; j--) {
int value = A[i][j];
// 利用右邊和下邊已經生產的資料來推出現在這個位置上右側和下方有多少個1
if(value==1){
if (j==N-1) {
rec[i][j][0] = 1;// 右側連續1的個數
}else {
rec[i][j][0] = rec[i][j+1][0]+1;
}
rec[i][j][1] = rec[i+1][j][1] + 1; // 向下連續1的個數
}
}
}
}
// O(n3)
static int solve(int [][]A){
int N = A.length;
int n = N;
while(n>0){
for (int i = 0; i < N; i++) {
if (i+n>N) break;
l3: // 這個語言相當於給第二層for迴圈命令 下面程式碼中的continue繼續的是第二層的for迴圈,不是while迴圈
for (int j = 0; j < N; j++) {
if (j+n>N) break;
if (check(i, j, n))
return n;
}
}
n--;
}
return 0;
}
// O(1)
private static boolean check(int i, int j, int n) {
// 左上角那個點往右數的1的數目要大於等於n
// 左上角那個點往下數的1的數目要大於等於n
// 右上角那個點往下數的1的個數要大於等於n
// 左下角那個點往右數的1的個數要大於等於n
if (rec[i][j][0]>=n&&rec[i][j][1]>=n&&rec[i][j+n-1][1]>=n&&rec[i+n-1][j][0]>=n) {
return true;
}
return false;
}
}
結果:
小結:
計算優化過後的時間複雜度為: 生成輔助陣列所花時間N2+後面迴圈遍歷時間N3,加起來整個演算法的時間複雜度就
為O(N3)了,那這個演算法的效率就比優化前的演算法的效率好太多了。
相關文章
- 第四章:多維陣列和矩陣 ------------- 4.8 子矩陣的最大累加和陣列矩陣
- 第四章:多維陣列和矩陣 ------------- 4.7 子陣列最大累加和陣列矩陣
- 第四章:多維陣列和矩陣 --------------- 4.1 基礎題:順時針列印二維陣列陣列矩陣
- 求二維陣列中最大子陣列的和陣列
- 第四章:多維陣列和矩陣 ------------- 4.3 基礎題:Z形列印二位陣列陣列矩陣
- 資料結構之陣列和矩陣--矩陣&不規則二維陣列資料結構陣列矩陣
- 矩陣和陣列矩陣陣列
- #1502 : 最大子矩陣矩陣
- 第四章:多維陣列和矩陣 ----------- 4.2 基礎題:將0所在的行列清零陣列矩陣
- 多維陣列轉一維陣列(降維的多種方式)陣列
- 多維陣列陣列
- PHP中二維陣列與多維陣列PHP陣列
- JavaScript 學習筆記 - 多維陣列變為一維陣列JavaScript筆記陣列
- 53. 最大子陣列和陣列
- C/C++ 二維陣列的理解(多維陣列)C++陣列
- Java陣列宣告建立和使用以及多維陣列、Arrays類、稀疏陣列Java陣列
- 資料結構:陣列,稀疏矩陣,矩陣的壓縮。應用:矩陣的轉置,矩陣相乘資料結構陣列矩陣
- JavaSE 陣列:一維陣列&二維陣列Java陣列
- 一維多維陣列陣列
- 多維陣列排序陣列排序
- Java break、continue 詳解與陣列深入解析:單維陣列和多維陣列詳細教程Java陣列
- [Python手撕]最大子陣列和Python陣列
- LeetCode53. 最大子陣列和LeetCode陣列
- 指標陣列和陣列指標與二維陣列指標陣列
- 二維陣列和稀疏陣列互轉陣列
- Multik——Kotlin的多維陣列Kotlin陣列
- jquery裡遍歷普通陣列和多維陣列的方法及例項jQuery陣列
- 演算法-陣列與矩陣演算法陣列矩陣
- 簡單介紹Lua一維陣列與多維陣列的使用陣列
- PHP 多維陣列排序PHP陣列排序
- 【力扣】最大子陣列和(貪心)力扣陣列
- 巨大的矩陣(矩陣加速)矩陣
- Leetcode 陣列中和為給定值的最長子陣列LeetCode陣列
- 【陣列】1394. 找出陣列中的幸運數(簡單)陣列
- JS陣列遍歷和獲取陣列最值JS陣列
- js 一維陣列轉二維陣列JS陣列
- js 二維陣列轉一維陣列JS陣列
- NumPy快餐教程(1) – 如何生成多維陣列陣列