从零学算法1017

1017. 负二进制转换
给你一个整数 n ,以二进制字符串的形式返回该整数的 负二进制(base -2)表示。
注意,除非字符串就是 “0”,否则返回的字符串中不能含有前导零。
示例 1:
输入:n = 2
输出:“110”
解释:(-2)2 + (-2)1 = 2
示例 2:
输入:n = 3
输出:“111”
解释:(-2)2 + (-2)1 + (-2)0 = 3
示例 3:
输入:n = 4
输出:“100”
解释:(-2)2 = 4
提示:
0 <= n <= 109

  • 我的原始人解法:最朴素的思路。
    1. 确定至少需要用几位表示 n ,并且得到这几位能表示的最大值 sum。根据定义会发现,由于 -2 的奇数次只会对最大值产生负影响,所以比如 3 位负二进制数和 4 位负二进制数的最大值其实都为 101(-22 + -20) 和 0101(-22 + -20)。
    2. 用负二进制先表示出来,最大值当然是把会得到负数的每一位取 0,其他位取 1,比如 101,10101。
    3. 我们在最大值的基础上减到等于 n,rest = sum - n 就是我们需要在步骤 2 的基础上减去的数,比如 rest = 7,我们总共需要减少 7。
      1. 先得到小于 7 的最大的 2x,即 4,我们需要先减去 4。(减去一个数就相当于把原本 4 对应的那一位取反即可,比如 101 要减 4 -> 001,101 要减 2 -> 111。)
      2. 减了 4 以后,还剩 3 没减,还是同理,先减 2,最后减 1。
  •   public String baseNeg2(int n) {
          int i = 0;
          int sum = 1;
          while(sum < n){
              i += 2;
              sum += Math.pow(2, i);
          }
          StringBuilder sb = new StringBuilder();
          while(i >= 0){
              if(i-- % 2 == 0)sb.append("1");
              else sb.append("0");
          }
          int rest = sum - n;
          while(rest > 0){
              int x = getDigit(rest);
              char c = sb.charAt(sb.length() - 1 - x);
              String newC = c == '1'? "0" : "1";
              sb.replace(sb.length() - 1 - x, sb.length() - x, newC);
              rest -= Math.pow(2, x);
          }
          return sb.toString();
      }
      // 得到小于 n 的最大的 2 的 x 次对应的那一位
      public int getDigit(int n){
          int sum = 0;
          int i = 0;
          while(sum < n){
              sum += Math.pow(2, i++);
          }
          return i - 1;
      }
    
  • 他人题解:看了很多篇题解,个人认为最本质的还是这个。
  • 首先 10 进制转 k 进制,原理都为不断取余,我们顺着这一点解决。
  • 当 k 为正数时,我们很容易写出模版,不断取余,拼接余数,然后 n 整除 k
  • 	if(n == 0)return "0";
        int k = 2;
        StringBuilder sb = new StringBuilder();
        while(n != 0){
            int r = n % k;
            sb.append(r);
            n = n / k;
        }
        return sb.reverse().toString();
    
  • 但是当 k 为负数时,余数可能出现负数,那肯定有问题了,比如将十进制的 19 转为 -9 进制,我们会得到如下
    • 19 ÷ −9 = −2 余 1
    • -2 ÷ −9 = 0 余 -2
  • 这里正确的步骤其实应该确保余数大于等于 0,即:
    • 19 ÷ −9 = −2 余 1
    • -2 ÷ −9 = 1 余 7 (-9 * 1 + 7 = -2 -> 7 = -2 + 9)
    • 1 ÷ -9 = 0 余 1
  • 根据第二步的 7 = -2 + 9 所以为了保证余数大于 0,余数应该为 n % k + abs(k),但这是余数为负数的情况;考虑到余数为正数时这样一加结果都大于 k 了,我们最终还是得对 abs(k) 取余,这样当余数为负数时,n % k + abs(k)abs(k) 取余还是 n % k + abs(k);当余数为正数时 (n % k + abs(k)) % abs(k) 其实就还等于 n % k
  • 同理,整除后的结果也需要处理,即 -2 ÷ −9 = 1 余 7 的 1 是怎么来的,我们换位得到 1 = (-2 - 7) ÷ -9,即 (n - r)/k,由于 r 本来就是 n / k 之后的余数,所以 (n - r) / k 等于 n / k
  •   public String baseNeg2(int n) {
          if(n == 0)return "0";
          int k = -2;
          StringBuilder sb = new StringBuilder();
          while(n != 0){
              int r = n % k;
              r = (r + Math.abs(k)) % Math.abs(k);
              sb.append(r);
              n = (n - r) / k;
          }
          return sb.reverse().toString();
      }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值