因此自然可以想到将 n 转换为 -2 的幂数和,即此时 n = \sum\limits_{i=0}^{m}C_{i} \times (-2)^{i,由于 -2 进制的每一位只能为 0 或 1,需要对每一位进行加法「进位 」运算即可得到完整的「负二进制」数。对于「负二进制」数,此时需要思考一下进位规则。对于 C\times (-2)^i,期望得到如下变换规则:
如果 C 为奇数则需要将等式变为 C \times (-2)^i = a \times (-2)^{i + 1} + (-2)^{i,此时第 i 位为 1,第 i + 1 位需要加上 a;
如果 C 为偶数则需要将等式变为 C \times (-2)^i = a \times (-2)^{i + 1,此时第 i 位为 0,第 i + 1 位需要加上 a;
根据以上的变换规则,只需要求出 a 即可。假设当前数位上的数字为 val,当前的位上保留的余数为 r,在 x 进制下的进位为 a,根据「进位 」的运算规则可知 val} = a \times x + r,此时可以得到进位 a = \dfrac{\textit{val}-r}{x。根据题意可知,「负二进制」数的每一位上保留的余数为 0 或 1,因此可以计算出当前的余数 r,由于在有符号整数的均采用补码表示,最低位的奇偶性保持不变,因此可以直接取 val 的最低位即可,此时可以得到 r = \textit{val} \And 1。根据上述等式可以知道,当前数位上的数字为 val 时,此时在「负二进制」下向高位的进位为 a} = \dfrac{\textit{val}-(\textit{val} \And 1)}{-2。 基于以上进位规则,将变换出来的数列进行进位运算即可得到完整的「负二进制」数。整个转换过程如下:
将 n 转换为二进制数,并将二进制数中的每一位转换为「负二进制」中的每一位,变换后的数列为 bits;
funcbaseNeg2(n int)string { if n == 0 { return"0" } bits := [32]int{} for i := 0; i < 32 && n != 0; i++ { if n&1 > 0 { bits[i]++ if i&1 > 0 { bits[i+1]++ } } n >>= 1 } carry := 0 for i := 0; i < 32; i++ { val := carry + bits[i] bits[i] = val & 1 carry = (val - bits[i]) / -2 } pos := 31 res := []byte{} for pos >= 0 && bits[pos] == 0 { pos-- } for pos >= 0 { res = append(res, byte(bits[pos])+'0') pos-- } returnstring(res) }
复杂度分析
时间复杂度:O(C),其中 C = 32。需要对 n 转换为二进制位,需要的时间复杂度为 O(\log n),然后需要对其进行二进制位的中每一位进行「负二进制进位」运算,由于整数有 32 位,因此需要「负二进制进位」运算 32 次即可。
空间复杂度:O(C),其中 C = 32。需要对 n 转换为二进制位,由于整数最多只有 32 位,在此每次采取固定的存储空间为 O(32)。
方法二:进制转换
思路与算法
当基数 x > 1 时,将整数 n 转换成 x 进制的原理是:令 n_0 = n,计算过程如下:
当计算第 0 位上的数字时,此时 n_1 = \Big\lfloor \dfrac{n_0}{x} \Big\rfloor,n_0 = n_1 \times x + r,其中 0 \le r < x;
当计算第 i 位上的数字时,此时 n_{i + 1} = \Big\lfloor \dfrac{n_i}{x} \Big\rfloor,n_i = n_{i+1} \times x + r,其中 0 \le r < x;
按照上述计算方式进行计算,直到满足 n_{i} = 0 结束。
如果基数 x 为负数,只要能确定余数的可能取值,上述做法同样适用。由于「负二进制」表示中的每一位都是 0 或 1,因此余数的可能取值是 0 和 1,可以使用上述做法将整数 n 转换成「负二进制」。具体转换过程如下:
如果 n = 0 则返回 0",n = 1 则直接返回 1”;
如果 n > 1 则使用一个字符串记录余数,将整数 n 转换成「负二进制」,重复执行如下操作,直到 n = 0;
计算当前 n 的余数,由于当前的余数只能为 0 或 1,由于有符号整数均采用补码表示,最低位的奇偶性保持不变,因此可以直接取 C 的最低位即可,此时直接用 n \And 1 即可得到最低位的余数,将余数拼接到字符串的末尾。
将 n 的值减去余数,然后将 n 的值除以 -2。
上述操作结束之后,将字符串翻转之后得到「负二进制」数。
代码
[sol2-C++]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
classSolution { public: string baseNeg2(int n){ if (n == 0 || n == 1) { returnto_string(n); } string res; while (n != 0) { int remainder = n & 1; res.push_back('0' + remainder); n -= remainder; n /= -2; } reverse(res.begin(), res.end()); return res; } };
[sol2-Java]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
classSolution { public String baseNeg2(int n) { if (n == 0 || n == 1) { return String.valueOf(n); } StringBuilderres=newStringBuilder(); while (n != 0) { intremainder= n & 1; res.append(remainder); n -= remainder; n /= -2; } return res.reverse().toString(); } }
[sol2-C#]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
publicclassSolution { publicstringBaseNeg2(int n) { if (n == 0 || n == 1) { return n.ToString(); } StringBuilder sb = new StringBuilder(); while (n != 0) { int remainder = n & 1; sb.Append(remainder); n -= remainder; n /= -2; } StringBuilder res = new StringBuilder(); for (int i = sb.Length - 1; i >= 0; i--) { res.Append(sb[i]); } return res.ToString(); } }
char * baseNeg2(int n) { if (n == 0) { return"0"; } if (n == 1) { return"1"; } char *res = (char *)calloc(sizeof(char), 32); int pos = 0; while (n != 0) { int remainder = n & 1; res[pos++] = '0' + remainder; n -= remainder; n /= -2; } for (int l = 0, r = pos - 1; l < r; l++, r--) { char c = res[l]; res[l] = res[r]; res[r] = c; } return res; }
[sol2-Python3]
1 2 3 4 5 6 7 8 9 10 11
classSolution: defbaseNeg2(self, n: int) -> str: if n == 0or n == 1: returnstr(n) res = [] while n: remainder = n & 1 res.append(str(remainder)) n -= remainder n //= -2 return''.join(res[::-1])
[sol2-JavaScript]
1 2 3 4 5 6 7 8 9 10 11 12 13
var baseNeg2 = function(n) { if (n === 0 || n === 1) { return'' + n; } let res = ''; while (n !== 0) { const remainder = n & 1; res += remainder; n -= remainder; n /= -2; } return res.split('').reverse().join(''); };
[sol2-Golang]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
funcbaseNeg2(n int)string { if n == 0 || n == 1 { return strconv.Itoa(n) } res := []byte{} for n != 0 { remainder := n & 1 res = append(res, '0'+byte(remainder)) n -= remainder n /= -2 } for i, n := 0, len(res); i < n/2; i++ { res[i], res[n-1-i] = res[n-1-i], res[i] } returnstring(res) }
复杂度分析
时间复杂度:O(\log n),其中 n 是给定的整数。整数 n 对应的「负二进制」表示的长度是 \log n,需要生成「负二进制」表示的每一位。
空间复杂度:O(1)。除返回值外,不需要额外的空间。
方法三:数学计算
思路与算法
根据题意可知,32 位「负二进制」数中第 i 位则表示(-2)^i,当 i 为偶数时,则 (-2)^i = 2^i,当 i 为奇数时,则 (-2)^i = -2^i,因此可以得到其最大值与最小值分别为如下:
根据以上推论可以知道,「负二进制」减法等同于 maxVal} \oplus \textit{diff。按照上述方法可以知道 n 的「负二进制」数等于 maxVal} \oplus (\textit{maxVal} - n),我们求出 n 的「负二进制」数,然后将其转换为二进制的字符串即可。
代码
[sol3-C++]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classSolution { public: string baseNeg2(int n){ int val = 0x55555555 ^ (0x55555555 - n); if (val == 0) { return"0"; } string res; while (val) { res.push_back('0' + (val & 1)); val >>= 1; } reverse(res.begin(), res.end()); return res; } };
[sol3-Java]
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classSolution { public String baseNeg2(int n) { intval=0x55555555 ^ (0x55555555 - n); if (val == 0) { return"0"; } StringBuilderres=newStringBuilder(); while (val != 0) { res.append(val & 1); val >>= 1; } return res.reverse().toString(); } }
[sol3-C#]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
publicclassSolution { publicstringBaseNeg2(int n) { int val = 0x55555555 ^ (0x55555555 - n); if (val == 0) { return"0"; } StringBuilder sb = new StringBuilder(); while (val != 0) { sb.Append(val & 1); val >>= 1; } StringBuilder res = new StringBuilder(); for (int i = sb.Length - 1; i >= 0; i--) { res.Append(sb[i]); } return res.ToString(); } }
[sol3-C]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
char * baseNeg2(int n) { int val = 0x55555555 ^ (0x55555555 - n); if (val == 0) { return"0"; } char *res = (char *)calloc(sizeof(char), 32); int pos = 0; while (val) { res[pos++] = '0' + (val & 1); val >>= 1; } for (int l = 0, r = pos - 1; l < r; l++, r--) { char c = res[l]; res[l] = res[r]; res[r] = c; } return res; }
[sol3-Python3]
1 2 3 4 5 6 7 8 9 10
classSolution: defbaseNeg2(self, n: int) -> str: val = 0x55555555 ^ (0x55555555 - n) if val == 0: return"0" res = [] while val: res.append(str(val & 1)) val >>= 1 return''.join(res[::-1])
[sol3-JavaScript]
1 2 3 4 5 6 7 8 9 10 11 12
var baseNeg2 = function(n) { let val = 0x55555555 ^ (0x55555555 - n); if (val === 0) { return"0"; } let res = ""; while (val !== 0) { res += val & 1; val >>= 1; } return res.split('').reverse().join(''); };
[sol3-Golang]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
funcbaseNeg2(n int)string { val := 0x55555555 ^ (0x55555555 - n) if val == 0 { return"0" } res := []byte{} for val > 0 { res = append(res, '0'+byte(val&1)) val >>= 1 } for i, n := 0, len(res); i < n/2; i++ { res[i], res[n-1-i] = res[n-1-i], res[i] } returnstring(res) }
复杂度分析
时间复杂度:O(\log n),其中 n 是给定的整数。整数 n 对应的「负二进制」表示的长度是 \log n,需要生成「负二进制」表示的每一位。