给定一个数字找到下一个稀疏数字

时间:2016-09-02 23:54:16

标签: algorithm bit-manipulation

问题陈述如下:

给定数字x,找到大于或等于x的最小稀疏数 如果在其二进制表示中没有两个相邻的1,则数字是稀疏的。例如5(二进制表示:101)是稀疏的,但是6(二进制表示:110)不是稀疏的。

我从this post处理问题,其中列出了最有效的解决方案,其运行时间为O(logn):

  

1)找到给定数字的二进制数并将其存储在a中      布尔数组。   2)将last_finalized位的初始化为0。   2)开始从最低有效位遍历二进制。       a)如果我们得到两个相邻的1,那么下一个(或第三个)          比特不是1,那么             (i)在此1之后使所有位最后完成                  位(包括最后确定的)为0。             (ii)将最后完成的位更新为下一位。

帖子中没有明确的含义是"最终的位。"似乎算法首先通过使用while循环将数字的二进制表示插入std::vector来开始,其中它将输入(数字x)与1进行ANDS然后将其推回到向量中但是至少从提供的描述中,不清楚为什么这样做。 是否有更明确的解释(甚至方法)来解决此问题的有效解决方案?

修改

// Start from second bit (next to LSB)
    for (int i=1; i<n-1; i++)
    {
       // If current bit and its previous bit are 1, but next
       // bit is not 1.
       if (bin[i] == 1 && bin[i-1] == 1 && bin[i+1] != 1)
       {
            // Make the next bit 1
            bin[i+1] = 1;

            // Make all bits before current bit as 0 to make
            // sure that we get the smallest next number
            for (int j=i; j>=last_final; j--)
                bin[j] = 0;

            // Store position of the bit set so that this bit
            // and bits before it are not changed next time.
            last_final = i+1;
        }
    }

2 个答案:

答案 0 :(得分:3)

如果在数字的二进制表示中看到任何序列“011”,则将“0”更改为“1”并将其后的每一位设置为“0”(因为它给出了最小值)。

该算法建议从右侧(最低位)开始,但如果从左侧开始,找到最左侧的序列“011”,并按上述方式执行,您将获得解决方案的一半时间。另一半是当该序列左边的下一位为'1'时。当您将'0'更改为'1'时,您创建一个需要以相同方式处理的新“011”序列。

“最后确定的位”是最左边的'0'位,其右侧仅看到'0'位。这是因为所有这些'0'都不会在接下来的步骤中改变。

答案 1 :(得分:0)

所以这里有一些观察来解决这个问题:-

现在将数字转换为二进制格式,如果最后一位数字是 0,那么我们可以在末尾附加 1 和 0,但如果最后一位数字是 1,那么我们只能在末尾附加 0。 所以天真的方法是做一个简单的迭代并检查每个数字,但如果我们仔细观察一些例子,我们可以优化这种方法

let say n=5 -> 101 next sparse is 5 (101)
let say n=14 -> 1110 next sparse is 16 (10000)
let say n=39 ->100111 next sparse is 40 (101000)
let say n=438 -> 110110110 next sparse is 512 (1000000000)

为了优化简单的方法,这里的想法是使用 BIT-MANIPULATION 这个概念是,如果我们将一个位序列与其自身的移位版本进行 AND 运算,我们就可以有效地从每个连续 1 的序列中删除尾随的 1。

for n=5
  0101 (5)
& 1010 (5<<1)
---------
  0000

因此,当您将 n&(n<<1) 的值设为零时,意味着您拥有的数字中没有任何连续的 1(因为如果它不为零,则必须有一系列连续的 1)我们的号码)所以这将是答案

for n=14 
  01110 (14)
& 11100 (14<<1)
----------------
  01100 

所以值不为零,然后将我们的数字增加 1,所以我们的新数字是 15 现在再次执行相同的操作

  01111 (15)
& 11110 (15<<1)
------------------------------
  01110

再次我们的数字不为零,然后将数字增加 1 并在 n = 16 时执行相同的操作

   010000 (16)
&  100000 (16<<1)
------------------------
   000000

所以现在我们的数字变为零,所以我们现在遇到一个不包含任何连续 1 的数字,所以我们的答案是 16。

因此,您也可以以类似的方式检查其他号码。 希望你明白了,如果是这样,然后upvote。快乐编码!

int nextSparse(int n) {
        // code here
        while(true)
        {
            if(n&(n<<1))
            n++;
            else
            return n;
        }
    }

时间复杂度为 O(logn)。