leetcode算法学习-进制-位运算

395 阅读8分钟

1.进制

二进制

Binary,缩写bit 比特
二进制 逢2进1, 只有0或1
只有32位 ,最前面一位,0 表示正数,1表示负数\

//代表 1 
0000 0000 0000 0000 0000 0000 0000 0001

//代表-1 
1000 0000 0000 0000 0000 0000 0000 0001

//js 表达  二进制0b开头
let a = Ob0001 ,或者 Ob00000000000000000000000000000001

八进制

Octal,缩写OCT或O
八进制就是逢8进1,都是0~7的数字,到8就进1位,八进制,
以8为基数的计数法,采用0,1,2,3,4,5,6,7八个数字,逢八进1。

//代表 1 
0000 0000 0000 0000 0000 0000 0000 0001

//代表-1 
1000 0000 0000 0000 0000 0000 0000 0001

//js 表达  二进制0o开头
let a = Oo0017 ,或者 Oo00000000000000000000000000000001

十进制

由0,1,2、3、4、5、6、7、8、9组成,运算规律是逢十进一;

十六进制

hex, 缩写x
由数字0~9以及字母A,B,C,D,E,F组成,运算规律是逢十六进一

//代表 1 
0000 0000 0000 0000 0000 0000 0000 0001

//代表-1 
1000 0000 0000 0000 0000 0000 0000 0001

//js 表达  二进制0x开头
let a = Ox001a 

进制转化

十进制转换其他进制

NumberObject.toString(radix);

//转二进制
var num = 18;
console.log(num.toString(2)); //10010

var num = -18; //负号保留
console.log(num.toString(2)); //-10010

//转8进制
var num = 18;
console.log(num.toString(8)); //22

var num = -18; //负号保留
console.log(num.toString(2)); //-22

//转16进制
var num = 1200;
console.log(num.toString(16));//4b0

其他进制 转十进制

parseInt(string, radix);

//二进制 转 十进制
parseInt("00100",2) //4

//8进制 转 十进制
parseInt("0011",8) // 9 

//16进制 转 十进制
parseInt("0010",16) // 16
parseInt("00f",16) // 15

2.二进制负数 相加

要做 5 - 5 = 0,其实相当于 5 + (-5) = 0 所以只要相加的时候 先做一下转化就ok了

一般要转化为两数的 补码 相加。最终使得相加之和 变成最大数 + 1,如 二进制0b1111111111,再加1,让他溢出,但其他位都已经是 ob000000000,所以结果为0.

原码

符号位 最前面保留的一位,1为负数,0为正数。
原码=符号位+真值。
比如:
[+5]原码=0 0000000 00000000 00000000 00000101
[-5]原码=1 0000000 00000000 00000000 00000101

如果上面直接相加,肯定不等于 0

反码

正数的反码与其原码相同;负数的反码是对其原码逐位取反 [+5]反码=0 0000000 00000000 00000000 00000101
[-5]反码=1 1111111 11111111 11111111 11111010\

补码

补码:正数的补码与原码相同,负数的补码等于其反码的末位加1。
[+5]反码=0 0000000 00000000 00000000 00000101
[-5]反码=1 1111111 11111111 11111111 11111011\

特殊情况:
+0 和 -0 的表示,在原码和反码都有两种形式,但是补码却只有一种:
[+0]=[0000 0000]原=[0000 0000]反=[0000 0000]补
[-0]=[1000 0000]原=[1111 1111]反=[0000 0000]补

测试5 - 5

[+5]补码=0 0000000 00000000 00000000 00000101
[-5]补码=1 1111111 11111111 11111111 11111011
由于相加后,在最前面33位进1,其他都为0,由于只有32位,33位的1溢出,结果还是0,所以5-5 结果为0.

3.位运算-基础篇

  • 与(&)
  • 或(|)
  • 非(~)
  • 异或(^)
  • 左移(<<)
  • 右移(>>)
  • 无符号右移(>>>)。

与(&)

两个都为1,才能为1,否则为0

A     = 10001001 
B     = 10010000
A & B = 10000000

或(|)

其中一个为1都为1

A     = 10001001
B     = 10010000
A | B = 10011001

非(~)

所有位取反

A     = 10001001
~A    = 01110110

异或(^)

两个一样则为0,不一样则为1

A   = 10001001
B   = 10010000
A^B = 00011001

左移(<<)

整体左移n位,等价于乘2*n,超出 32 位的值,则自动丢弃

A     =  10001001
A<<1   = 100010010

右移(>>)

整体右移n位,等价于除于2*n ,有符号右移位

A     =  10001001
A>>1   =   1000100

//实际是 
0000 0000 0000 0000 0000 0000 1000 1001
//右移 前面第一位0保留
0000 0000 0000 0000 0000 0000 0100 0100

//如果是负数 
1000 0000 0000 0000 0000 0000 1000 1001
//右移 前面第一位1保留
1000 0000 0000 0000 0000 0000 0100 0100

无符号右移(>>>) 右移后左边空出的位用零来填充。移出右边的位被丢弃
当都是正数时,等价于 >> ,当是负数的时候,就会完全不一样。

A       = 10001001
A>>>1   = 01000100

//如果是负数 
1000 0000 0000 0000 0000 0000 1000 1001
//右移 前面第一位1保留
0100 0000 0000 0000 0000 0000 0100 0100

2.react源码的位运算

export type SideEffectTag = number;

// Don't change these two values. They're used by React Dev Tools.
export const NoEffect = /* */ 0b000000000000;
export const PerformedWork = /* */ 0b000000000001;

// You can change the rest (and add more).
export const Placement = /* */ 0b000000000010;
export const Update = /* */ 0b000000000100;
export const PlacementAndUpdate = /* */ 0b000000000110;
export const Deletion = /* */ 0b000000001000;
export const ContentReset = /* */ 0b000000010000;
export const Callback = /* */ 0b000000100000;
export const DidCapture = /* */ 0b000001000000;
export const Ref = /* */ 0b000010000000;
export const Snapshot = /* */ 0b000100000000;
export const Passive = /* */ 0b001000000000;

// Passive & Update & Callback & Ref & Snapshot
export const LifecycleEffectMask = /* */ 0b001110100100;

// Union of all host effects
export const HostEffectMask = /* */ 0b001111111111;

export const Incomplete = /* */ 0b010000000000;
export const ShouldCapture = /* */ 0b100000000000;

通过或运算就可以 增加权限如或检验

  1. 新增权限 -> 修改
let permission = Placement //0b000000000010
//只需要把 permission 和 Update 做或运算
ret = permission | Update
// 等价于 求或
// 0b000000000010
// 0b000000000100 
// 0b000000000110  
  1. 验证权限 -> 通过求 &
let ret = 0b000000000010 //假设 只有加权限
if(ret & Update == 0) { //校验是否有修改权限
// 等价于  求与
// 0b000000000010
// 0b000000000100 
// 0b000000000000 == 0 //确实没有权限
} 

3.刷题

136. 只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

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

示例 2:

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

答题

  1. for + map
/**
 * @param {number[]} nums
 * @return {number}
 */
var singleNumber = function(nums) {
    let map = new Map()
    for (let i = 0; i < nums.length; i++) {
        const item = nums[i];
        if(map.has(item)) {
            map.set(item,true)
        }else {
            map.set(item,false)
        }
    } 
    for (item of map ) {
        if(!item[1]) {
            return item[0]
        } 
    }
}; 
  1. 遍历+异或 由于本题重复的一定是两个一样的,再加上异或有以下特性:
  • a ^ 0 = a
  • a ^ a = 0
  • a ^ a ^ b = b
var singleNumber = function(nums) {
    let ret = 0
    for (let i = 0; i < nums.length; i++) {
        ret = ret ^ nums[i];
    }
    return ret
};