【问题标题】:Given a number find the next sparse number给定一个数字,找到下一个稀疏数字
【发布时间】:2016-09-02 23:54:15
【问题描述】:

问题陈述如下:

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

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

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

帖子中不清楚的是“最终确定的位”是什么意思。似乎该算法首先使用 while 循环将数字的二进制表示插入到 std::vector 中,在该循环中,它将输入(即数字 x)与 1 进行与运算,然后将其推回向量中,但是,在至少从提供的描述来看,尚不清楚为什么要这样做。 对于这个问题的有效解决方案有更清晰的解释(甚至方法)吗?

编辑:

// 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;
        }
    }

【问题讨论】:

  • 尝试在纸上手动执行算法,看看它是如何运作的。
  • "finalized" 在这种情况下意味着您已经计算了它的最终值。所以“最终位”是您知道其最终值的最后一位的位置。这意味着您可以在算法的下一次迭代中从那里开始,因为之前的位不会改变。
  • 该解释省略了算法的一个重要部分,即在第 2.a 部分下的某处将下一位设置为 1。

标签: algorithm bit-manipulation


【解决方案1】:

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

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

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

【讨论】:

  • 既然有两个for循环,为什么运行时间还是O(n)?
  • 没有嵌套循环。你做的唯一循环是当“011”序列左边的下一位是“1”时。如果是这种情况,您将得到另一个“011”序列,前一个序列的左侧 2 位。此外,如果 n 是位数,则运行时复杂度为 O(n)。您链接到的帖子说 O(log x) 因为 x 本身就是数字,表示 x 所需的位数是 log2(x)。
  • 我刚刚用我所指的代码 sn-p 更新了帖子。每次当前和前一个位为 1 且 i +1 为 0 时,都有一个嵌套的 for 循环将位从 i 更改回 last_final。此外,帖子中的算法是从右到左(即最小到最大)值位)。
  • 好吧,您展示的算法遍历整个位数组一次以找到“011”序列,并将每个位设置为“0”最多一次。在最坏的情况下,它将每个位设置为“0”一次(最高有效位除外),因此这些操作的数量受 n (位数)的限制。最终,整个算法执行了 O(n) 次操作。
  • 哦,好吧,所以因为嵌套循环没有将每个 ("n") 位设置为 0(除了在最坏的情况下,您实际上只是将当前位和前一个位设置为零) ,整体线性迭代将渐近地占主导地位。
【解决方案2】:

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

现在将数字转换为二进制格式,如果最后一个数字是 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 这个概念是,如果我们将一个位序列与自身的移位版本进行与运算,我们实际上是从每个连续 1 的序列中删除了尾随的 1。

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

所以当你得到 n&(n

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。

因此,您也可以以类似的方式检查其他号码。 希望你能明白,如果是这样,那就投票吧。快乐编码!

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

时间复杂度为 O(logn)。

【讨论】:

    猜你喜欢
    • 2022-11-10
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    • 2012-09-23
    • 2012-03-11
    • 2012-04-02
    • 1970-01-01
    • 2012-01-29
    相关资源
    最近更新 更多