【ビットオペレーション】アルゴリズム戦闘


1. アルゴリズム原理

コンピュータ内のデータはバイナリ形式で保存および処理され、ビット操作はバイナリ ビットに対して直接操作されます。一般的なビット演算子には、and ( &)、or ( |)、排他的論理和 ( ^)、否定 ( ) 、~左シフト ( <<)、右シフト ( >>) などが含まれます。

ここに画像の説明を挿入


一般的なビット演算のまとめ

  1. 基础位运算
    ここに画像の説明を挿入
  2. 给一个数n,确定它的二进制表示中第x位是0还是1
    ここに画像の説明を挿入
  3. 将一个数n的二进制表示的第x位修改成1
    ここに画像の説明を挿入
  4. 将一个数n的二进制表示的第x位修改成0
    ここに画像の説明を挿入
  5. 位图的思想
    ここに画像の説明を挿入
  6. 提取一个数(n)二进制表示中最右侧的1
    ここに画像の説明を挿入
  7. 干掉一个数(n)二进制表示中最右侧的1
    ここに画像の説明を挿入
  8. 位运算的优先级: 可能であれば括弧を追加してください
  9. 异或(^)运算的运算律
    ここに画像の説明を挿入

2.アルゴリズム戦闘

1. Leetcode のインタビューの質問 01.01. キャラクターがユニークかどうかを判断する

ここに画像の説明を挿入
キャラクターがユニークかどうかを判断する

問題解決のアイデア: ビットマップのアイデア

ビットマップのアイデアを使用してこの質問を解決できます。質問では、文字列には小文字のみが含まれているため、ビットマップの 32 ビットとして機能するには int 型の整数のみが必要であることが示されているためです。

まず、ビットマップのすべてのビット位置を 0 に設定します。これにより、デフォルトの文字列の文字がビットマップに表示されなくなります。次に、文字列全体を走査して、その文字がビットマップに出現したかどうかを検出します。現在の文字がビットマップに出現していない場合は、現在の文字が初めて出現することを意味します。このとき、ビットマップ内での位置を設定します。 ( 当前字符-'a') これを 1 に設定し、文字列を逆方向にトラバースするだけで、文字が再び出現した場合、その文字がすでに出現していることがビットマップから検出できます。すべてのfalse文字を走査した後ですべての文字が 1 回だけ出現することが判明した場合、それは文字列が質問の意味を満たしていることを意味します。

コード:

class Solution {
    
    
public:
    bool isUnique(string astr) {
    
    
        int num = 0;
        
        for(int i = 0; i < astr.size(); i++)
        {
    
    
            int index = astr[i] - 'a';
            if(((num>>index) & 1) == 1)
                return false;
            else
                num |= 1 << index;
        }
        return true;
    }
};

2. leetcode268 の桁が欠落しています

ここに画像の説明を挿入
欠番

問題解決のアイデア: XOR 演算

タイトルは、配列内の [1~n] に数値が欠落していることを示しているため、まず配列内の数値を加算し、次にその結果を [1~n] 内のすべて^^数値とXOR します。^この演算は交換と結合の法則を満たすため、ビット単位の ^ の結果では、欠落している数値が 1 回出現し、残りの数値が 2 回出現します。これらの数値を XOR すると、結果が欠落した数値になります。

ここに画像の説明を挿入

コード:

class Solution {
    
    
public:
    int missingNumber(vector<int>& nums) {
    
    
        int ret = 0;
        for(auto e : nums)
            ret ^= e;
        for(int i = 1; i <= nums.size(); i++)
            ret ^= i;
        return ret;
    }
};

3. leetcode371 2 つの整数の合計

ここに画像の説明を挿入
2 つの整数の合計

問題解決アイデア:XOR演算 - キャリーなしの加算

タイトルでは、演算子 sum を使用して 2 つの整数の合計を計算することはできないとしているため、整数 a と b の合計を a と b の合計に分割し、キャリーなしで+加算した-結果を得ることができます。2 つの整数を使用できます達成する、運ぶ その結果、達成するために使用できます例を挙げてみましょう:无进位相加结果进位结果的和^(a & b) << 1

ここに画像の説明を挿入

毎回a と b に无进位相加结果とを代入し进位结果的和、キャリーが 0 になるまでこの処理を繰り返します。最終的に a が必要な結果になります。表現できる範囲を超える値を unsigned 型に代入すると、結果は、unsigned 型で表現される値の総数を除算した初期値の剰余となります。したがって、符号なし型を使用してオーバーフローを防ぐことができます。

コード:

lass Solution {
    
    
public:
    int getSum(int a, int b) {
    
    
        while(b != 0)
        {
    
    
            int x = a ^ b; // 先算出无进位相加的结果
            unsigned int carry = (a & b) << 1; // 算出进位
            a = x;
            b = carry;
        }
        return a;
    }
};

4.leetcode004 一度だけ現れる数字Ⅱ


一度だけ現れる数 II

問題解決のアイデア:

配列内の各要素の各バイナリ ビットは 1 または 0 で、タイトルでは 1 つの要素のみが 1 回出現し、残りの要素は 3 回出現することがわかります。そのため、配列内の各要素に対してビット演算を使用して、xget (x >> i) & 1x それらを加算して 3 の余りを取得すると、結果は 0 または 1 になる必要があり、これが答えの i 番目のバイナリ ビットになります。

したがって、答えの i 番目のビットは、配列内のすべての要素の i 番目のビットの合計を 3 で割った余りになります。最初に 1 を初期化しret = 0、その余りの値に応じて、その数値に対応する 2 進数のビットを 0 に設定するか 1 に設定するかを判断できます。余りが 1 の場合は手動でビットを 1 に設定する必要があり、そうでない場合は、加工は必要ありませんret |= (1 << i)

コード:

class Solution {
    
    
public:
    int singleNumber(vector<int>& nums) {
    
    
        int ret = 0;
        for(int i = 0; i < 32; i++)
        {
    
    
            int cnt = 0;
            for(auto e : nums)
                cnt += ((e>>i)&1);
            if(cnt % 3)
                ret |= (1 << i);
        }
        return ret;
    }
};

5. Leetcode のインタビューの質問 17.19. 消える 2 つの数字

ここに画像の説明を挿入
数字が2つ足りない

問題解決のアイデア:

実はこの問題は、リコウの第260問で一度だけ出てくる数字Ⅲの考え方で解くことができ、具体的な解法は以下の通りです。

配列には 1 から N までのすべての整数が含まれていますが、2 つの数値が欠落しているため、配列の長さは n になります。したがって、配列に含まれるべき数値は [1, n + 2] になります。元の配列内の数値を [1, n + 2] と組み合わせることができ、問題を変換できます。配列内に 1 回しか出現しない 2 つの数値を見つけます。

整体异或この問題を解決するには、と の2 つのステップに分けることができます分组异或全体的な XOR: タイトルで指定された配列内のすべての数値と [1, n + 2] に含まれる数値の XOR を計算します。その結果は、1 回だけ出現する 2 つの数値の XOR 結果になりますa^b次に を抽出する必要があります该数字二进制表示中最低位的1位置が k 番目のビットであると仮定すると、すべての数値の要素を(题目中给出的数字和【1,n + 2】的数字)2 つのカテゴリに分けることができます。1 つは k 番目のビットが 0 であるすべての 2 進表現を含み、もう 1 つは k 番目のビットが 1 数値であるすべての 2 進表現を含みます。

  • すべての要素内で 2 回出現する要素の場合、その要素の 2 回の出現は同じクラスに含まれます。
  • すべての要素のうち 1 回だけ出現する要素 (x1 と x2) については、それらは異なるクラスに含まれます。

したがって、これら 2 種類の要素を個別に XOR すると、2 つの異なる結果が得られます。1 つの結果は x1 で、もう 1 つの結果は x2 です。これら 2 つの数値は 1 回だけ現れる 2 つの数値です。つまり、この質問では、欠落している 2 つの数値が私たちが見つける必要がある数字。

コード:

class Solution {
    
    
public:
    vector<int> missingTwo(vector<int>& nums) {
    
    
        int ret = 0;
        for(int i = 1; i <= nums.size()+2; i++)
            ret ^= i;
        for(auto e : nums) ret ^= e;
        int lowbit = ret & -ret; // 找出最后一个不同的比特位
        int ret1 = 0, ret2 = 0;
        for(int i = 1; i <= nums.size()+2; i++){
    
     // 分组异或
            if(lowbit & i) ret1 ^= i;
            else ret2 ^= i;
        }
        for(auto e : nums){
    
    
            if(lowbit & e) ret1 ^= e;
            else ret2 ^= e;
        }
        return {
    
    ret1, ret2};
    }
};

3. まとめ

ビット演算では数値の 2 進ビットを直接操作できるため、メモリが節約され、プログラムの実行がより速く、より信頼性が高くなります。アルゴリズムでビット演算を使用すると、アルゴリズムの時間の複雑さと空間の複雑さが大幅に軽減され、ビット演算を適切に使用すると、プログラムをより簡潔で美しくすることもできます。

おすすめ

転載: blog.csdn.net/m0_67595314/article/details/132397489