LeetCode 2つの正に順序付けられた配列の中央値を見つける(4つの質問)

LeetCode 2つの正に順序付けられた配列の中央値を見つける(4つの質問)

@author:Jingdai
@date:2021.03.14

トピックの説明(4つの質問)

それぞれ2つのサイズmn、正のシーケンス(小さいものから大きいもの)の配列nums1とが与えられますnums2これらの2つの正のシーケンス配列の中央値を特定して返してください

アイデアとコード

時間計算量を考慮しない場合は、2つの方法を簡単に考えることができます。1つは、最初に配列をマージしてから、中央値を並べ替えて見つけることです。時間計算量はO((m + n)* log(m + n)) ;もう1つは、配列の順序に従ってマージすることです。マージが順序付けられた配列になった後、中央値が計算され、時間計算量はO(m + n)です。これらの2つの考えやすい方法は、ここには記録されていません。

ここに画像の説明を挿入します

以下のために、示されているようにnums1nums2私たちの目的は、配列をソートするので、我々は唯一の境界上の要素を確認する必要がありますので、要素は、除行の右に左境界要素よりも小さくなるように、分割するラインを見つけることです要件を満たすための交差、つまり、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長さは非常に最初に行うことができます長いnums1nums2交流) 、次に、私たちの目標は、二分探索を使用しiて位置のインデックスを見つけることです。ここで、より短いnums1バイナリ検索を選択すると、2つの利点があります。最初の最も直接的な利点は、時間の複雑さが低いことです。nums1バイナリ検索を選択するとnums2、分割線に対応する必要があり、両側をトリミングできないように見えないという利点があります。要素の状況。

ここに画像の説明を挿入します

上に示したように、nums2二分探索として配列を選択しj場合、図の位置のようにi配列が見つからないため、右側の分割線の右側が4つの要素、左側が3つの要素を分割する必要があります。j合法性の位置を決定するための追加のコードを記述し、コーディングの複雑さも増すためnums1、バイナリ検索としてより短い配列を選択します

上記の状況は一般的な状況ですが、特別な状況があります。次の2つの例を見てください。

例1:

ここに画像の説明を挿入します

例2:

ここに画像の説明を挿入します

上記だけの2つの例は、推定することができるされii-1jおよび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);
    }
}

おすすめ

転載: blog.csdn.net/qq_41512783/article/details/114804567