从二进制开始
世界上有10种人,一种懂二进制,另一种不懂二进制。
十进制、0、1、2、3、4、5、6、7、8、9是十进制的10个基数。至于为什么我们使用十进制而不是其他几进制或几十进制,我认为可能是因为人类有10个手指。如果我们数到第十个,我们就不能数了。我们只能进入这个位置。与我们的人类思想相比,他们没有手指,只有电流(计算机)CPU、内存和其他设备由二极管三极管组成),相当于开关。只有两种状态(即真-假)两种状态。因此,计算机的物理特性决定了计算机语言采用二进制,0和1是二进制的两个基数。
相关术语
- ASCII (American Standard Code for Information Interchange):它是一个基于拉丁字母的计算机编码系统,主要用于显示现代英语和其他西欧语言。它是最常见的信息交换标准,相当于国际标准ISO/IEC 646。ASCII1967年首次以标准类型发表,1986年最后一次更新,到目前为止共定义了128个字符。ASCII 代码使用指定的7 位或8 128 或256 也就是说,我们最多使用8次可能的字符CPU所有字母、数字、符号机需要使用的所有字母、数字、符号等字符。
- Bit:比特-表示信息量最小的单位,一个电流的通电断电工作称为一位,即1位Bit
- byte:字节——二进制数据的单位。8次通电断电(也就是8bit)即可表示ASCII代码表中的任何字符,也就是说,我们计算机收到的指令中的每个字符都是1byte。1 byte = 8 Bit
- kb:1kb = 1024byte,1kb相当于1024个字符,基本上可以满足记事本上的小日记
- MB: 1MB = 1024kb,1MB自费约100万,相当于一首中等质量的音乐
- GB:1GB = 2024MB,1GB可以存储一部电影
分配给编译系统int型数据2字节/4字节(具体由C编译系统决定)TurboC2.0为每个整数类型分配2个字节(16个二进位)VisualC 分配4个字节(32个二进制位)。如果将两个字节分配到整形变量,存储单元中存储的最大值为01111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111115 - 1)即十进制数为32767,最小值为1万,万万万万,万万万万,万万万万,万万万万,万万万万,万万万万,万万万万,万万万万,万万万万,万万万,万万万万,万万万万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,万,15),即-32768,因此整形变量值的范围为-32768 ~ 超出此范围的32767会出现 数值的溢出。若分四个字节,范围为-231~ 231 - 1,即-2147483648~2147483647。
原码、反码、补码
原码:最高位为符号位,其余为数值本身的绝对值 反码:当值为正数时,与原码相同;当为负数时,符号位为1,其余位置取反原码(按位取反) 补码:当数值为正数时,与原码相同;当为负数时,符号位为1,其余位置反向原码,然后反向整个数 1,即反码 1.负数补码原码=补码的反码 1
?? 举例: 为了整洁,这里有一个字节,即8个比特表示二进制数。由于最高位为符号位,1字节能表示的十进制数范围为-127-127
十进制 | 原码 | 反码 | 补码 |
---|---|---|---|
3 | 0000 0011 | 0000 0011 | 0000 0011 |
-3 | 1000 0011 | 1111 1100 | 1111 1101 |
计算机数字运算是基于补码的。
javascript十进制转二进制的方法
注:该方法仅适用于十进制正整数
parseInt(127).toString(2) ---> 1111111 parseInt(43).toString(2) ---> 101011
位运算
常见的位运算符有:与(&)、或(|)、异或(^)、取反(~)、左移(<<)、右移(>>带符号右移 >>>右移动无符号)
- 位与(&):当两者都是1时,结果是1
- 位或(|):当两者都是0时,结果是0
- 异或(^): 当两者不同时,结果是1
- 取反(~):按位取反,0->1, 1->0
- 左移(<<): 放弃高位,补低位 0
- 右移(>>): 低位放弃,高位正数补充 0.负数补充1(负数右移操作时,在右移操作前请求负数原码补充)
?? 举例:
2 | -2 | 位运算结果 | 原码 | 十进制结果 | |
---|---|---|---|---|---|
二进制值 | 0000 0010 | 1000 0010 | |||
补码 | 0000 0010 | 1111 1110 | |||
& | 0000 0010 | 0000 0010 | 2 | ||
\ | 1111 1110 | 1000 0010 | -2 | ||
^ | 1111 1100 | 1000 0100 | -4 | ||
~ | 1111 1101 | 011 1101 |
奇技淫巧
- 设置x的第n位为1
x | (1<<n)
- 设置x的第n位为0
x & ~(1<<n)
- 将x的第n位取反
x ^ (1<<n)
- 得到最大的int
int maxInt = ~(1 << 31);
int maxInt = (1 << 31) - 1;
int maxInt = (1 << -1) - 1;
- 到最小的int
int minInt = 1 << 31;
int minInt = 1 << -1;
- 计算n乘以2
n << 1
- 计算n除以2
n >> 1
- 计算n乘以2的m次方
n << m
- 就算n除以2的m次方
n >> m
- 判断a和b是否相等
!(a^b)
- 判断n是否为奇数
(n & 1) == 1
- 交换a和b的值
//方式1
a ^= b;
b ^= a;
a ^= b;
//方式2
a = a ^ b ^ (b = a)
- 得到x的绝对值
(x ^ (x >> 31)) - (x >> 31)
- 得到a和b中较大的数
b & ((a-b) >> 31) | a & (~(a-b) >> 31)
- 得到a和b中较小的数
a & ((a-b) >> 31) | b & (~(a-b) >> 31)
- 判断a和b是否同正负
(x ^ y) >= 0
- 计算i的相反数
//方式1
i = ~i + 1
//方式2
i = (i ^ -1) + 1
- 判断n是否为2的若干次方
n > 0 && (n & (n - 1)) == 0
- 拿到x的位于最右的1位
x & (-x)
- 拿到x的位于最右的0位
~x & (x+1)
- 将x的位于最右的0位设置为1
x | (x+1)
- 计算n + 1
-~n
- 计算n - 1
~-n
- 将x(x为字符)转换为小写(如果原来就已经为小写则保持)
x | ' '
- 将x(x为字符)转换为大写(如果原来就已经为大写则保持)
x & '_'
- 大小写互换(x为字符)
x ^ ' '
- 得到x(x为字符)在字母表中的位置(大小写都适用)
x & '\x1F'
相关算法题解
1.【2的幂】
给定一个整数n,如果它是2的幂,则返回true,否则返回false ——【leetCode 231】
题目分析:如果是2的幂,则这个数一定大于等于0,并且转化为二进制数必然为1后面加若干个0。假设为1000,那么1000 - 1 = 0001。那么这个整数n满足:
题解:
/** * @param {number} n * @return {boolean} */
var isPowerOfTwo = function(n) {
return (n > 0 && (n & (n-1)) === 0)
};
2.【4的幂】
给定一个整数,写一个函数来判断它是否是 4 的幂次方。如果是,返回 true ;否则,返回 false 。 ——【leetCode 342】
题目分析:4x = 22x, 4的幂一定是2的幂,但是2的幂不一定是4的。 推理可得: 22x % 3 = 1; 22x+1 % 3 = 2;
题解:
/** * @param {number} n * @return {boolean} */
var isPowerOfFour = function(n) {
return n > 0 && (n & (n-1)) === 0 && n % 3 === 1
};
3.【位1的个数】
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。——【力扣191】
题目分析:二进制n 不断的位与n-1,最后得到的一定是0,那么位与的次数就是1的个数
题解:
/** * @param {number} n - a positive integer * @return {number} */
var hammingWeight = function(n) {
let count = 0
while(n) {
n &= n-1
count++
}
return count
};
4.【交换变量】
编写一个函数,不使用临时变量,交换两个变量
题目分析:任何数异或它本身等于0,异或0等于它本身
题解:
var swapNumbers = function(arr) {
arr[0] = arr[0] ^ arr[1]
arr[1] = arr[0] ^ arr[1]
arr[0] = arr[0] ^ arr[1]
return arr
};
5.【只出现一次的数字】
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。说明: 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?——【力扣136】
题目分析:异或的性质,a ^ a = 0; a ^ 0 = a; 满足交换律和结合律: a ^ b = b ^ a; (a ^ b) ^ c = a ^ (b ^ c);
题解:
/** * @param {number[]} nums * @return {number} */
var singleNumber = function(nums) {
return nums.reduce((res, item) => res^item)
// let res = 0
// for (i = 0; i < nums.length; i++) {
// res ^= nums[i]
// }
// return res
};
6.【汉明距离】
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。给你两个整数 x 和 y,计算并返回它们之间的汉明距离。——【力扣461】
题目分析:不同位异或为1,则此题转化为求两数字异或后1出现的次数
题解:
/** * @param {number} x * @param {number} y * @return {number} */
var hammingDistance = function(x, y) {
let z = x ^ y
let count = 0
while (z) {
z &= (z - 1)
count++
}
return count
};
7.【交替位二进制数】
给定一个正整数,检查它的二进制表示是否总是 0、1 交替出现:换句话说,就是二进制表示中相邻两位的数字永不相同。——【力扣693】
题目分析:01交替出现,01 & 11 = 01; 11&11=11; 00&11 = 00;所以只要将相邻两位不断位与11——即十进制数字3,结果出现3或者0则表示不是交替位二进制数,否则是。
题解:
/** * @param {number} n * @return {boolean} */
var hasAlternatingBits = function(n) {
while (n) {
if ((n & 3) === 0 || (n & 3) === 3) {
return false
}
n >>= 1
}
return true
}
8.【两整数之和】
给你两个整数 a 和 b ,不使用 运算符 + 和 - ,计算并返回两整数之和。——【力扣371】
题目分析:异或操作符:两个位不相同时为1,否则为0.这个操作符也可以理解为不带进位的加法。位与操作符,都为1时才为1,否则为0,因此&可以理解为只含进位加法。a + b = (a ^ b) + ((a & b) << 1);a ^ b是无进位的相加; a&b得到每一位的进位;让无进位相加的结果与进位不断的异或, 直到进位为0; 题解:
/** * @param {number} a * @param {number} b * @return {number} */
var getSum = function(a, b) {
while (b != 0) {
const carry = (a & b) << 1
a = a ^ b
b = carry
}
return a
// return b === 0 ? a : getSum(a ^ b, (a&b)<<1)
}