[Javaの基本的な知識]いくつかの一般的なビット演算

1.奇数と偶数を決定します

数値nが2進数として表される場合、最後の2進数が1であるか0であるかを判別するだけで済みます。1の場合は奇数、それ以外の場合は偶数を表します。コードは次のように表示されます。

if(n & 1 == 1){
    // n是奇数
}

2.2つの番号を交換します

x = x ^ y; // (1)
y = x ^ y; // (2)
x = x ^ y; // (3)

2つの同一の数の排他的論理和の結果が0、つまりn ^ n = 0であり、0とXORされた後、任意の数がそれ自体に等しいこと、つまりn ^ 0 = nであることは誰もが知っています。

したがって、(1)のxを(2)のxに代入すると、次のようになります。y= x ^ y =(x ^ y)^ y = x ^(y ^ y)= x ^ 0 = x値はに割り当てられますy。

(3)の場合、導出は次のようになります。x= x ^ y =(x ^ y)^ x =(x ^ x)^ y = 0 ^ y = yであるため、yの値はxに割り当てられます。

排他的論理和演算は、可換および結合法則をサポートします。

3.繰り返されていない番号を見つけます

整数データのセットを提供します。これらのデータのうち、1つの数字は1回だけ表示され、他の数字は2回表示されます。数字を見つけましょう

多くの人がハッシュテーブルを使用してこの質問を保存します。保存されるたびに、特定の数の出現回数が記録され、最後にハッシュテーブルがトラバースされて、1回だけ発生する出現回数が検出されます。この方法の時間計算量はO(n)であり、空間計算量もO(n)です。

実際、この問題はビット単位の算術でもあり得ます。すべての整数をXORすることができます。2つの同一の数のXORの結果は0であるため、0の数のXORの結果はそれ自体であるため、排他的論理和の結果ORは、1回だけ表示される番号です。たとえば、このデータセットは1、2、3、4、5、1、2、3、4です。5つは1回だけ表示され、他は2回表示されます。すべてをXORします。結果は次のとおりです。

1 ^ 2 ^ 3 ^ 4 ^ 5 ^ 1 ^ 2 ^ 3 ^ 4 =(1 ^ 1)^(2 ^ 2)^(3 ^ 3)^(4 ^ 4)^ 5 = 0 ^ 0 ^ 0 ^ 0 ^ 5 = 5

コードは次のように表示されます。

int find(int[] nums){
    int tmp = nums[0];
    for(int i = 1;i < nums.length; i++)
        tmp ^= arr[i];
    
    return tmp;
}

4、2のn乗

2のn乗で解き、システムに付属のpow関数を使用できません

この質問を見た多くの人は、n 2を掛けるだけで十分だと思うかもしれません。これを行うと、時間計算量はO(n)になります。では、ビット演算でどのようにそれを行うのでしょうか?

たとえば、n = 13、nの2進数は1101である場合、2の13乗は次のように分解できます。2^ 1101 = 2 ^ 0001 * 2 ^ 0100 * 2 ^ 1000。1101をビットごとに&1と>> 1で読み取ることができます。1の場合、このビットで表される乗数が最終結果に乗算されます。最終的なコードは次のとおりです。

int pow(int n) {
    int sum = 1;
    int tmp = 2;
    while(n != 0) {
        if(n & 1 == 1)
            sum *= tmp;
        
        temp *= temp;
        n >>= 1;
    }
    return sum;
}

5.N以下の2指数の最大の累乗を見つけます

たとえば、N = 19の場合、バイナリへの変換は00010011です(ここでは便宜上、8ビットのバイナリを使用して表現します)。次に、探している数値は、バイナリの左端の1を保持することであり、後続のすべての1は0になりますつまり、ターゲット番号は00010000です。では、この番号を取得する方法は?対応する解決策は次のとおりです。

1.左端の1を見つけて、右のすべての0を1に変更します

2.取得した値に1を加算すると、00100000、つまり000111111 + 1 = 00100000を取得できます。

3.取得した00100000を1桁右に移動して、00010000を取得します。つまり、00100000 >> 1 = 00010000になります。

だから問題は、最初のステップで左端の1の後に0を取得するにはどうすればよいですか?

コードは次のように表示されます。

n |= n >> 1;
n |= n >> 2;
n |= n >> 4;

これ、nを右にシフトしてOR演算を実行することで取得できます。説明させてください。左端の1が2桁のk番目の位置(左から右に数えて)にあり、nを1桁右にシフトした後、k +1番目の位置にあると仮定します。得られた結果も1でなければならず、次にnの結果と右シフトに対してOR演算を実行すると、得られた結果のk番目とk + 1桁は1でなければなりません。同様に、nを右にシフトします。 2ビットで、得られた結果のk桁目+2とk + 3位は1でなければならず、OR演算を再度実行すると、kth、k + 1、k + 2、k +3を取得できます。すべて1であり、以下同様です。

最終的なコードは次のとおりです。

int findN(int n){
    n |= n >> 1;
    n |= n >> 2;
    n |= n >> 4;
    n |= n >> 8 // 整型一般是 32 位,上面我是假设 8 位。
    return (n + 1) >> 1;
}

このアプローチの時間計算量は約O(1)です。

おすすめ

転載: blog.csdn.net/qq_41893274/article/details/112759540