LeetCode 2つの正に順序付けられた配列の中央値を見つける(4つの質問)
@author:Jingdai
@date:2021.03.14
トピックの説明(4つの質問)
それぞれ2つのサイズ
m
とn
、正のシーケンス(小さいものから大きいもの)の配列nums1
とが与えられますnums2
。これらの2つの正のシーケンス配列の中央値を特定して返してください。
アイデアとコード
時間計算量を考慮しない場合は、2つの方法を簡単に考えることができます。1つは、最初に配列をマージしてから、中央値を並べ替えて見つけることです。時間計算量はO((m + n)* log(m + n)) ;もう1つは、配列の順序に従ってマージすることです。マージが順序付けられた配列になった後、中央値が計算され、時間計算量はO(m + n)です。これらの2つの考えやすい方法は、ここには記録されていません。
以下のために、示されているようにnums1
とnums2
私たちの目的は、配列をソートするので、我々は唯一の境界上の要素を確認する必要がありますので、要素は、除行の右に左境界要素よりも小さくなるように、分割するラインを見つけることです要件を満たすための交差、つまり、nums1 [i-1] <= nums2 [j]およびnums2 [j-1] <= nums1 [i]が満たされます。
最初に2つの配列の全長を計算しますlen
。次に、2の各辺の長さに加えて要素の数を計算します。偶数のlen
場合、要素の両側が等しい場合len
は奇数になりますが、もう1つの要素があります。ここでは、正しい選択をもう1つ行うため、len/2
常に左側の要素の境界数になります。同時に、図にあります。下i
付き文字と上付き文字j
は、配列の左側に対応するいくつかの要素を表す場合もあります。つまり、I + J = len / 2と描画できます。
その後、アルゴリズムのコアは、アレイがソートされているので、あなたが分割ラインを見つけるためにバイナリ検索を使用することができ、そこで、我々は、配列の短い長さを選択したnums1
(あればnums1
長さは非常に最初に行うことができます長いnums1
とnums2
交流) 、次に、私たちの目標は、二分探索を使用しi
て位置のインデックスを見つけることです。ここで、より短いnums1
バイナリ検索を選択すると、2つの利点があります。最初の最も直接的な利点は、時間の複雑さが低いことです。nums1
バイナリ検索を選択するとnums2
、分割線に対応する必要があり、両側をトリミングできないように見えないという利点があります。要素の状況。
上に示したように、nums2
二分探索として配列を選択したj
場合、図の位置のようにi
配列が見つからないため、右側の分割線の右側が4つの要素、左側が3つの要素を分割する必要があります。j
合法性の位置を決定するための追加のコードを記述し、コーディングの複雑さも増すためnums1
、バイナリ検索としてより短い配列を選択します。
上記の状況は一般的な状況ですが、特別な状況があります。次の2つの例を見てください。
例1:
例2:
上記だけの2つの例は、推定することができるされi
、i-1
、j
およびj-1
おそらくアレイの境界を超えている、ように、特別な処理を必要とi
等しいnums1
長一方nums[i]
値Integer.MAX_VALUEで、i
その結果、0に等しいnums[i-1]
値でありますInteger.MIN_VALUE ;j
コードにもよりますが、同じことが言えます。
最後に、上記から特殊なケースを見ることができます。i
[0、nums1.length]の値はすべての要件を満たしているため、-1の長nums1
さではなく正しい長さの初期値になりますnums1
。
参照コードは次のとおりです。
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if (nums1.length > nums2.length) {
int[] temp = nums2;
nums2 = nums1;
nums1 = temp;
}
int len1 = nums1.length;
int len2 = nums2.length;
int len = len1 + len2;
int halfLen = len / 2; // left count
int left = 0;
int right = len1;
while (left < right) {
int i = (left + right) / 2;
int j = halfLen - i;
/**
** s1 nums1[i-1] ------ s2 nums1[i]
** s3 nums2[j-1] ------ s4 nums2[j]
**/
int s1 = i-1 < 0 ? Integer.MIN_VALUE : nums1[i-1];
int s2 = i == len1 ? Integer.MAX_VALUE : nums1[i];
int s3 = j-1 < 0 ? Integer.MIN_VALUE : nums2[j-1];
int s4 = j == len2 ? Integer.MAX_VALUE : nums2[j];
if (s1 <= s4 && s3 <= s2) {
left = i;
break;
} else if (s1 <= s4) {
// s3 > s2
left = i + 1;
} else {
right = i-1;
}
}
int s1 = left-1 < 0 ? Integer.MIN_VALUE : nums1[left-1];
int s2 = left == len1 ? Integer.MAX_VALUE : nums1[left];
int s3 = halfLen-left-1 < 0 ? Integer.MIN_VALUE : nums2[halfLen-left-1];
int s4 = halfLen-left == len2 ? Integer.MAX_VALUE : nums2[halfLen-left];
if (len % 2 == 0) {
return (double) (Math.min(s2, s4) + Math.max(s1, s3)) / 2.0;
} else {
return (double) Math.min(s2, s4);
}
}