毎日 N 個のアルゴリズムの高頻度質問 - DAY2

毎朝本を読むことができない場合は、クイズだけでもやってください。このブログは夜にお送りしていますが、

トピック 1

 sum で構成される 2 次元グリッドが 与えられた場合 0 、 境界がすべて で構成される 最大の 正方形のサブグリッドを見つけて  、サブグリッド内の要素の数を返してください。存在しない場合は、 例: 01111 01001 01001 01111 01011ここで、境界線がすべて 1 である最大の正方形のサイズは 4*4 なので、4 を返します。1grid10






まず第一に、N*N の面積に O(N の 4 乗) 程度の長方形がいくつあるかを理解する必要があります。

そして、正方形の数の大きさは O(N³) で、正方形の走査の複雑さは O(N)、つまり、暴力的な解法の時間計算量は O(N の 4 乗) です。 。

この質問では依然として配列を前処理する方法が使用されています

各位置の右側に有効長が 1、その下に有効長(固定小数点と辺の長さを使用して最大値を決定)がある場合、それは各位置の平方長ではないでしょうか。 

すると小さな加速度が生じますが、現在点が0のときは正方形の固定点であることは絶対にあり得ません。

OK、大きなプロセスは完了しました。具体的な実装を見てみましょう

各点の右下への有効長を取得するにはどうすればよいですか? 直接横断することはできません。これは暴力的な解決策と同じではありませんか?

ここでは、動的プログラミングを使用して 2 つの dp 配列を作成することにしました。1 つは次の dp 配列を保存し、もう 1 つは右側の dp 配列を保存します。

右の場合、各グリッドの結果は右の結果に依存します。

以下の場合、各グリッドの結果は次の結果に依存します。

0 1 1 1 1 たとえば、赤色の位置の場合、右側の有効な数値は、右側のグリッドの数 + 1 です。右側のグリッドは、上のグリッドに応じて、0 1 0 0 1 と同じになります  
。右側に 1 を加えたものなので、最も多くのものを取得するだけで済みます。右側の列の結果を使用して、すべての結果を推定できます。 0 1 0 0 1 0 1 1 1 1 次に、その下の有効な数値は、有効な数値に
依存
ます。 1 の下 + 1 など、最後の行 0 のみが
必要です 1 0 1 1 結果

(誰が尋ねたのか? 何十回も読まれているブログに誰が尋ねたのか? 私は自分自身に尋ねた) では、現在位置が 0 の場合は +1 を追加する必要がありますか? これは、この位置を頂点として正方形を作成することは不可能であることを意味します。位置データを直接0に設定

次に、これら 2 つの補助配列を使用して、各状況を横断することができます (サイズを横断する可能性はとにかく非常に多く、最大でも N 個です)

SIZE を見つけるために各ポイントをトラバースする場合、行と列の数の特殊なケースは何ですか?最初は非常に間違っていることがわかりました)

public static void setBorderMap(int[][] m, int[][] right, int[][] down) {
		int r = m.length;
		int c = m[0].length;
		if (m[r - 1][c - 1] == 1) {
			right[r - 1][c - 1] = 1;
			down[r - 1][c - 1] = 1;
		}
		for (int i = r - 2; i != -1; i--) {
			if (m[i][c - 1] == 1) {
				right[i][c - 1] = 1;
				down[i][c - 1] = down[i + 1][c - 1] + 1;
			}
		}
		for (int i = c - 2; i != -1; i--) {
			if (m[r - 1][i] == 1) {
				right[r - 1][i] = right[r - 1][i + 1] + 1;
				down[r - 1][i] = 1;
			}
		}
		for (int i = r - 2; i != -1; i--) {
			for (int j = c - 2; j != -1; j--) {
				if (m[i][j] == 1) {
					right[i][j] = right[i][j + 1] + 1;
					down[i][j] = down[i + 1][j] + 1;
				}
			}
		}
	}

	public static int getMaxSize(int[][] m) {
		int[][] right = new int[m.length][m[0].length];
		int[][] down = new int[m.length][m[0].length];
		setBorderMap(m, right, down); // O(N^2); + 
		
		for (int size = Math.min(m.length, m[0].length); size != 0; size--) {
			if (hasSizeOfBorder(size, right, down)) {
				return size*size;
			}
		}
		return 0;
	}

	public static boolean hasSizeOfBorder(int size, int[][] right, int[][] down) {
		for (int i = 0; i != right.length - size + 1; i++) {
			for (int j = 0; j != right[0].length - size + 1; j++) {
				if (right[i][j] >= size && down[i][j] >= size
						&& right[i + size - 1][j] >= size
						&& down[i][j + size - 1] >= size) {
					return true;
				}
			}
		}
		return false;
	}
    public static int largest1BorderedSquare(int[][] grid) {
	     return getMaxSize(grid);
	    }

歩いていてふと思い出したのですが、大から小までサイズをトラバースする意味は加速であり、小から大まですべてのサイズをトラバースする必要がある場合は、大から小までトラバースするだけで最初の結果が得られます。

トピック 2

長さ M の配列を生成します。a<b<c が abc の任意のセットを満たし、arr[a]+arr[c] ! =2arr[b] を満たすと仮定します。 

この質問、正直に言うと、空飛ぶ妖精が機械的に質問を下げているような気がします

まず、隣接する 3 つの数 1 5 3 が存在するという原理について話しましょう。この条件はこの条件を満たしますか?その後、対応する最初の奇数、5 番目の奇数、3 番目の奇数も満たしますか?数学的証明 a + c != 2b then 2a -1 + 2c - 1 ?= 2(2*b-1) これを単純化すると、まだ条件が満たされていることがわかります。 

この場合、最初の偶数、5 番目の偶数、3 番目の偶数もこの条件を満たします。たとえば、a + c != 2b なので、2a+2c!=4b となります。 

これら 3 つの奇数と 3 つの偶数をもう一度組み合わせてみましょう。奇数、奇数、偶数、偶数、奇数と奇数は満たされます。偶数と偶数は満たされます。奇数と偶数の間には何かがあることが証明されました。奇数 + 偶数は奇数。2b は偶数である必要があるため、条件を満たさなければなりません

このように 3 つと 3 つの数を数えることができることは明らかです。最初の奇数、5 番目の奇数、3 番目の奇数は 1、9、5 です。その後、最初の奇数、9 番目の奇数になります。数字と 5 番目の奇数。類推すると、3 つのグループは 1 なので、どんな数字でも表現できます。

次に、three とthree のスペルを理解すると、配列のスペルを理解できるようになります。非常に良かったです。私は大間違いでした。なぜなら、これは奇数のグループが偶数のグループである場合にのみ当てはまるからです。グループの場合奇数の集合が偶数の集合である場合、奇数の集合は絶対的に真ではありません

したがって、私たちのアイデアは、長さ 7 の配列が必要だと追加しましょう。その後、長さ 8 を生成してから切り詰めます。

8、つまり、4 つの奇数と 4 つの偶数のグループ、つまり、長さ 4 の arr[a]+arr[c] のグループが必要です! =2arr[b] arr[a]+ with長さは 4 arr[c] ! =2arr[b] 2 を生成するには 2 が必要、1 を生成するには 1 が必要であり、それ自体が 1 です

1 は最初の奇数を生成します 最初の偶数を生成します

1 2  

最初の奇数を生成する 2 番目の奇数を生成する 最初の偶数を生成する 2 番目の偶数を生成する

1 3 2 4

1、3、2、4 番目の奇数と 1、3、2、4 番目の偶数を生成します

……

その後切り詰めます

public static int [] createArr(int N) {

		if(N==1) {
			return new int [] {1};
		}
		int half = (N+1)/2; //当N为奇数的时候 按偶数生成 当N为整数时 多的一个1也不影响结果 8+1/2 = 4;
		int [] base = createArr(half); 
		int [] res = new int [N];
		for(int i = 0;i<half;i++) {
			res[i] = base[i]*2-1;//如果N是奇数前一半肯定不影响 后一半也好办 直接生成到N
		}
		for(int i = half;i<N;i++) {
			res[i] = base[i-half]*2;
		}
		return res;
	}

トピック 3

バイナリ ツリーのヘッド ノードが与えられた場合、パスには 3 つの異なるルールがあります。1
) パスはヘッド ノードから開始し、リーフ ノードで終了する必要があり、最大パスを返す必要があります。2
) パスは任意のノードから開始できます。 、ただし、「任意のノードに移動し、最大パスを返す」に進む必要があります
。3) パスは任意のノードから開始し、任意のノードに移動し、最大パスを返すことができます。

質問1

このシンプルな 

	public static int Maxlength(Node head) {
		if(head==null) {
			return 0;
		}
		int left = Maxlength(head.left);
		int right = Maxlength(head.right);
		int max = Math.max(left, right);
		return max+head.value;
	}

質問2

ここで負の数の問題を考える必要がありますが、もう 1 点進むとパスの合計が小さくなる可能性があります。

class Info{
	int allTreeMaxSum = 0;
	int fromHeadMaxSum = 0;
	public Info(int no,int yes) {
		allTreeMaxSum = no;
		fromHeadMaxSum = yes;
	}
}

public static Info Maxlength(Node head) {
		if(head==null) {
			return new Info(0, 0);
		}
		Info left = Maxlength(head.left);
		Info right = Maxlength(head.right);
		int p1 = left.allTreeMaxSum;
		int p2 = right.allTreeMaxSum;
		int p3 = head.value;
		int p4 = left.fromHeadMaxSum + p3;
		int p5 = right.fromHeadMaxSum + p3;
		int allTreeMaxSum = Math.max(Math.max(Math.max(p1, p2),Math.max(p3, p4)),p5);
		int fromHeadMaxSum = Math.max(Math.max(p4,p5),p3);
		return new Info(allTreeMaxSum, fromHeadMaxSum);
	}

コードの説明ですが、ここでの Info は各ノードが上位ノードに送信するデータを指します。

allTreeMaxSum は現在のパスの最大値を指します
。fromHeadMaxSum は前のパスの合計を指します。

なぜこんなに分けているのかというと、その前に不要なノードがあると、それ以降のノードが利用できなくなるため、この点が欲しい場合は、それより前のノードすべてが必要とするデータが必要になるからです(修正は不要です)必要なのは前のノードの数だけであるように見えるかもしれないため) 2 つのノードの場合、この状況は p3 と比較することによって実現されます。たとえば、5 -1、5 が最大である限り、3 5 - が形成されます。次の点では 1 になります。その場合、現在の fromHeadMaxSum は 3 5 であるため、p3 の状況は、現在の点が最大である場合だけを扱うのではなく、現在のノードに近いいくつかのノードの場合のみを扱います。これは線形です。終点によってパスが大きくなるのを望まない場合は、それを永久に破棄できます)

そして、この allTreeMaxSum は、これまでに出現したすべての可能性 (この点は必須であり、その点は必要ありません) を参照しているため、この数値は現在のノードの参加を必要としない可能性があります。

5つの状況に分かれています

1. 左のツリーに現れた最大値

2. 右側のツリーに表示された最大値

3. 左側のツリーの現在の長さ (現在のノードを含む)   

4. 右ツリーの現在の長さ (現在のノードを含む)

このパス上のノードが常に正の数であることが本当であれば、1 と 3、2 と 4 の値は実際に同じです。

5. これは最も特殊なケースで、ツリー上に正の数が 1 つだけある場合、他はすべて負の数であるため、私自身がノードになります。

(今回の件は無視してるだけです)

3 4 の結果は 1 2 に含まれていませんか? なぜ 3 4 が必要なのでしょうか? この数値を保持しておかないと、この先頭を含むパスを計算したい場合に前のパスの合計を見つけることができないためです。ノード。 

質問3

この問題では、曲がる状況を考慮する必要がありますが、当然、何度も通過したノードを再び歩くことはできません。

私たちは依然としてケースバイケースで話し合っています

1. ヘッダーは含まれません

   a. 左側のツリーの現在の最大パス

   b. 右ツリーの現在の最大パス  

2. ヘッダーが含まれます

   a. このノード + 左のツリーのフルパス

  b. このノード + 右のツリーのフルパス

  c. このノード + 左右のツリーのフルパス (訂正: フルパスではなく、現在のノードが向かう最大のパスが p3 の制御によって実現されます)

  d. ノード自体

(ヘッダーを含まないものには制限はありません (そのサブツリーの最大値は、パッケージにヘッダーが含まれないかどうかには影響しません。とにかく、ヘッダーを含むものはすでに制限されています) ヘッダーを含むもののみ)

p6は間違いなくそうなるだろう 

今回は隅の関係でfromHeadMaxSumにp6を含めることができません

class Info{
	int allTreeMaxSum = 0;
	int fromHeadMaxSum = 0;
	public Info(int no,int yes) {
		allTreeMaxSum = no;
		fromHeadMaxSum = yes;
	}
}

class Solution {
	public Info Maxlength(TreeNode root) {
		if(root==null) {
			return null;//在看到输入为[-3]的时候 突然明白为啥 这里要直接返回空了(刚开始返回的是new Info(0,0)) 对应要改的话 p1....的初始值都要改成MIN 如果没有就当做最小处理
		}
		Info leftInfo = Maxlength(root.left);
		Info rightInfo = Maxlength(root.right);
		int p1 = Integer.MIN_VALUE;
		if (leftInfo != null) {
			p1 = leftInfo.allTreeMaxSum;
		}
		int p2 = Integer.MIN_VALUE;
		if (rightInfo != null) {
			p2 = rightInfo.allTreeMaxSum;
		}
		int p3 = root.val;
		int p4 = Integer.MIN_VALUE;
		if (leftInfo != null) {
			p4 = root.val + leftInfo.fromHeadMaxSum;
		}
		int p5 = Integer.MIN_VALUE;
		if (rightInfo != null) {
			p5 = root.val + rightInfo.fromHeadMaxSum;
		}

		int p6 = Integer.MIN_VALUE;
		if (leftInfo != null && rightInfo != null) {
			p6 = root.val + leftInfo.fromHeadMaxSum + rightInfo.fromHeadMaxSum;
		}
		int allTreeMaxSum = Math.max(Math.max(Math.max(p1, p2),Math.max(p3, p4)),Math.max(p5, p6));
		int fromHeadMaxSum = Math.max(Math.max(p4,p5),p3);
		return new Info(allTreeMaxSum, fromHeadMaxSum);
	}
    public int maxPathSum(TreeNode root) {
       return Maxlength(root).allTreeMaxSum;
    }
}

LeetCode の元の質問 これはLeetcode のリンクです 

おすすめ

転載: blog.csdn.net/chara9885/article/details/131673173