Likouブラッシングノート:338。ビットカウント(4つのソリューション、ブルートフォース方式->メモリ検索方式->動的計画法、4番目のビット演算を強くお勧めします。レイヤーごとに理解しやすい)

トピック:

338.ビットカウント
負でない整数numが与えられます。0≤i≤numの範囲の各数値iについて、2進数の1の数を数え、それらを配列として返します。

例1:

入力:2
出力:[0,1,1]

例2:

入力:5
出力:[0,1,1,2,1,2]

上級:

時間計算量がO(n * sizeof(integer))の解を与えるのは非常に簡単です。しかし、線形時間O(n)で1回のスキャンでそれを行うことができますか?
——————————————————————————————————
アルゴリズムのスペースの複雑さはO(n)である必要があります。
———————————————————————————————————
ソリューションをさらに改善できますか?この操作を実行するには、組み込み関数(C ++の__builtin_popcountなど)をC ++またはその他の言語で使用しないでください。

方法1:暴力

問題解決のアイデア:
これは、考えて、0からnumまでの各数値をトラバースし、各数値の2進数で1の数を数える最も簡単な方法である必要があります。

複雑さ:
時間の複雑さ:O(N * sizeof(int))
スペースの複雑さ:O(1)、返された配列はスペースの複雑さには含まれません。

問題解決のPythonコード:

class Solution:
    def countBits(self, num: int) -> List[int]:
        res = []
        for i in range(num + 1):
            res.append(bin(i).count("1"))
        return res

方法2:メモリ検索方法

問題解決策のアイデア:
上記の暴力的な解決策では、実際には多くの繰り返し計算があります。たとえば、i = 8の場合、i = 4、2、1、0が必要であり、これらの値はすでに計算されているため、メモリ検索を使用できます。

いわゆるメモリ検索は、再帰関数が終了するたびに計算結果を保存することです。この場合、次に再帰するときに同じ入力が検出された場合は、計算を再度繰り返すことなく、保存された結果から直接クエリを実行して戻ります。

たとえば、i = 8の場合、i = 4の状況が必要であり、i = 4の状況は以前に計算されているので、memo [4]を直接返すだけです。

複雑さ:
時間計算量:O(N)、これは1回トラバースされ、各ソリューションは以前に記憶された結果から見つけることができるためです。
スペースの複雑さ:O(N)、結果を保存するために補助スペースを使用すると、スペースの結果はO(N)になります。

問題解決のPythonコード:

class Solution(object):
    def countBits(self, num):
        self.memo = [0] * (num + 1)
        res = []
        for i in range(num + 1):
            res.append(self.count(i))
        return res
    
    def count(self, num):
        if num == 0:
            return 0
        if self.memo[num] != 0:
            return self.memo[num]
        if num % 2 == 1:
            res = self.count(num - 1) + 1
        else:
            res = self.count(num // 2)
        self.memo[num] = res
        return res

方法3:動的計画法

実際、多くの場合、動的計画法は記憶された検索から最適化されます。この問題も同じである可能性があります。

方法2検索をメモ化するプロセスでは、再帰関数が呼び出されるたびに、再帰関数が1回だけ実行され、メモによってキャプチャされて返されることがわかります。実際、再帰関数を削除して、res配列から直接結果を確認できます。

同時に、伝達方程式の式は、answer [i] = answer [i >> 1] +(i&1)として最適化されます。

したがって、次の動的計画法を取得します。

複雑さ:
時間計算量:O(N)O(N)、1回トラバースされるため。
スペースの複雑さ:O(1)O(1)、返された結果が占めるスペースは、スペースの複雑さには含まれません。

問題解決のPythonコード:

class Solution:
    def countBits(self, num):
        res = [0] * (num + 1)
        for i in range(1, num + 1):
            res[i] = res[i >> 1] + (i & 1)
        return res

著者:fuxuemingzhu
リンク:https://leetcode-cn.com/problems/counting-bits/solution/yi-bu-bu-fen-xi-tui-dao-chu-dong-tai-gui-3yog/
出典:force LeetCode(LeetCode)https://leetcode-cn.com/problems/counting-bits/

方法4:ビット演算(強く推奨)

問題解決のアイデア:考え方
を変えることができます。iの「ビット数」を計算するときに、0≤j<iの「ビット数」がある場合、jの「ビット数」は既知です。そして、iはjよりもバイナリ表現が多いだけです。1の場合、iの「1ビット数」をすばやく取得できます。

ビット[i]がiiの「ビット数」を表すとすると、上記の関係は次のように表すことができます。[j] + 1bits [i] = bits [j] +1。

正の整数xの場合、y≤xでyが2の整数乗であるように、最大​​の正の整数yがわかっている場合、yのバイナリ表現の最上位ビットのみが1で、残りはすべて0です。このとき、yはのx「最上位ビット」と呼ばれます。z = x−y、明らかに0≤z<xとすると、bits [x] = bits [z] +1となります。

正の整数が2の整数乗であるかどうかを判断するには、方法1で説明したビット単位のAND演算の性質を使用できます。正の整数yが2の整数乗である場合、yの2進表現の最上位ビットのみが1であり、残りは0であるため、y&(y-1)= 0です。y&(y-1)= 0の場合に限り、正の整数yは2の整数乗であることがわかります。

明らかに、0の「ビット数」は0です。highBitを使用して現在の最上位ビットを表し、11からnumまでの各正の整数iiをトラバースし、次の操作を実行します。

i&(i-1)= 0の場合、highBit = iを設定し、現在の最上位ビットを更新します。

iはi-highBitの「ビット数」より11多いです。各数値は小さいものから大きいものへとトラバースされるため、iにトラバースするとき、i-highBitの「ビット数」は既知です。bits[i] =ビット[i-highBit] +1。

結果の配列ビットが答えです。

問題解決のPythonコード:

class Solution:
    def countBits(self, num: int) -> List[int]:
        bits = [0]
        highBit = 0
        for i in range(1, num + 1):
            if i & (i - 1) == 0:
                highBit = i
            bits.append(bits[i - highBit] + 1)
        return bits

著者:LeetCode-ソリューション
リンク:https://leetcode-cn.com/problems/counting-bits/solution/bi-te-wei-ji-shu-by-leetcode-solution-0t1i/
ソース:滞在ボタン(LeetCode)https://leetcode-cn.com/problems/counting-bits/

おすすめ

転載: blog.csdn.net/weixin_44414948/article/details/114323843
おすすめ