简单动态规划的实现(Leetcode 338. 比特位计数)

背景

想到五月就是蓝桥杯决赛了,而自己还是什么都不会的渣渣,感觉还是得花点时间找找代码感觉。听人说蓝桥杯基本上动态规划加贪心算法基本就够省二的水平了,因此还是想从动规入手。

题目描述

题目来源于Leetcode 338.比特位计数。

官方描述
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。

示例 1:

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

示例 2:

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

进阶:

给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗?
要求算法的空间复杂度为O(n)。
你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的 __builtin_popcount)来执行此操作。

题目分析

第一眼看到这个题以为是要我从计算二进制的角度去找计算的规律,但是稍微列了两三个数就发现没有必要,规律十分明显。

在这里插入图片描述
简单计算出了16个数就能够直观的看出来,前23个数中,后2 2个数的1的个数恰好都是前22个数的1的个数加1得到的。当然对于8、4、2个数也成立。

其实从数学的角度也完全可以分析出规律,任何一个数n,都能写成2i+k的形式,这里的k是n对2i取余的值,而2i的二进制一定是1000…的形式,即1的个数是1。因此n的二进制的1的个数都等于k的二进制的1的个数加上1。

程序

想明白了规律之后代码就迎刃而解。但是花费空间与时间复杂度极大。

    public int[] countBits(int num) {
        int []result = new int[num+1];
        int i,j=0;
        result[0] = 0;
        for(i=1;i<num+1;i++)
        {
          if(Math.pow(2,j+1)<=i)
         {
          j++;
         }
         result[i] = result[i-(int)Math.pow(2,j)]+1;
        }
     return result;
    }

运行成功了但是花费的复杂度惨不忍睹。
在这里插入图片描述

优化

优化的时候看了看其他人的代码,感觉以后做算法题还是不能调用函数来做,尽可能用基本的代码实现,最好还是用c。蓝桥结束之后换成c再刷Leetcode。再有就是感到大佬太强了,打死我我也想不出这种方法。
优化的角度主要在二进制计算上,对于n位的二进制串,取其高n-1位,则此n位二进制串中1的个数或者等于n-1位二进制串中1的个数,或者等于n-1位二进制串中1的个数(取决于最后一位是否为1)。

 public int[] countBits(int num) {
        int []result = new int[num+1];
        int i;
        result[0] = 0;
        for(i=1;i<num+1;i++)
        {
         result[i] = result[i>>1]+(1&i); //(1&i)表示最后一位的值(1或0)
        }
     return result;
    }

在这里插入图片描述
这次时间花费只消耗了3ms,但是内存消耗还是很可怕。不是很清楚应该怎么解决内存消耗。

猜你喜欢

转载自blog.csdn.net/qq_41748559/article/details/89045835