位运算和数学

进制

任何一种进位计数制都有一个基数,基数为 X的进位计数制称为 X进制,表示每一个数位上的数运算时都是逢 X 进一。

在这里插入图片描述

常见进制

  1. 十进制:0,1,2,3,4,5,6,7,8,90,1,2,3,4,5,6,7,8,9。

  2. 二进制:0,10,1。

  3. 八进制:0,1,2,3,4,5,6,70,1,2,3,4,5,6,7。

  4. 十六进制: 0,1,2,3,4,5,6,7,8,90,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F

进制转换

非十进制转十进制

将非十进制数转成十进制数,只要将每个数位的加权和即可。
在这里插入图片描述

十进制转非十进制

将十进制数转成 XX 进制数,需要对整数部分和小数部分分别转换。

  1. 对于整数部分,转换方式是将十进制数的整数部分每次除以 X直到变成 0,并记录每次的余数,反向遍历每次的余数即可得到 X进制表示。

  2. 对于小数部分,转换方式是将十进制数的小数部分每次乘以 X直到变成 0,并记录每次的整数部分,正序遍历每次的整数部分即可得到 X 进制表示。

其他进制间的转换

  1. 在两个不同的非十进制之间转换,常规的思路是先转成十进制数,再转成目标进制数。在一些特殊情况下,也可以不经过十进制,直接进行转换。
  2. 例如,将二进制数转成八进制数或十六进制数,以及将八进制数或十六进制数转成二进制数,都不需要经过十进制。一位八进制数可以表示成三位二进制数,一位十六进制数可以表示成四位二进制数。
    在这里插入图片描述

计算机中的二进制

  1. 计算机采用的是二进制,二进制包括两个数码:0,1。
  2. 一位二进制数的可能取值有 2 个,k位二进制数的可能取值就有 2^k个。

在计算机中有多种数据类型,表示整数的数据类型就有好几种:

1 字节数,即 8 位二进制数,可能取值有 2^8个;
2 字节数,即 16 位二进制数,可能取值有 2^16个;
类推......

32位机,寻址范围2^32,最大只能4G虚拟内存就是这个原因

有符号整数和无符号整数

  1. 计算机中的数据类型包括有符号类型(整数)和无符号类型(正整数),有符号类型的整数称为有符号整数,无符号类型的整数称为无符号整数。

  2. 有符号整数中,最高位用于表示符号,因此最高位又称符号位。当最高位是 0时表示 0或正整数,当最高位是 1时表示负整数。除了最高位以外的数位用于表示数字的大小。

  3. 无符号整数中,所有的数位都用于表示数字的大小,因此无符号整数不存在负数。
    在这里插入图片描述

原码、补码和反码

机器数和真值

  1. 机器数:一个数在计算机中的二进制表示形式(有符号)
  2. 真值:机器数的真正数值
  3. 机器数的最高位是符号位,所以机器数的形式值不一定等于真正数值。

例如 10001010 的形式值是 138,真正数值是 -10

原码、反码和补码的概念

  1. 原码:机器数的符号位加上机器数的真值的绝对值,最高位是符号位,其余位表示数值。
    以 8位二进制数为例。+10的原码是 00001010,-10的原码是10001010。

  2. 反码
    0和正数的反码与原码相同,负数的反码是将原码的除了符号位之外的每一位取反。
    对于负数,反码的表示方式不直观,通常需要转换成原码才能计算其数值。

  3. 补码
    0和正数的补码与原码、反码相同,负数的补码是在反码的基础上加 1 得到。
    对于负数,补码的表示方式不直观,通常需要转换成原码才能计算其数值。

  4. 计算机中的表示

    • 反码的引入,解决了原码的减法运算结果错误的问题,但是仍然没有解决同时存在 +0 和 -0 的问题。
    • 补码的引入则同时解决了减法运算错误和同时存在 +0 和 -0的问题,而且可以多表示一个最小值。
    • 在补码表示法中,不存在 -0的情况。以 8位二进制数为例,0的补码是 00000000,10000000 表示的是 -128−128

位运算

位运算共有 6种,分别是:与、或、异或、取反、左移和右移,其中左移和右移统称移位运算,移位运算又分为算术移位和逻辑移位。

func main(){
	a:=0b10
	b:=0b11
	fmt.Println(a)
	fmt.Println(b)

	fmt.Println(a&b) //与
	fmt.Println(a|b) //或
	fmt.Println(a^b) //异或

	fmt.Println(^a) //反
	2
	3
	2
	3
	1
	-3
}

与、或、异或和取反

  1. 与运算的符号是 &,运算规则是:对于每个二进制位,当两个数对应的位都为 1 时,结果才为 1,否则结果为 0。
  2. 或运算的符号是 |,运算规则是:对于每个二进制位,当两个数对应的位都为 0 时,结果才为 0,否则结果为 1。
  3. 异或运算的符号是 ∧ ,运算规则是:对于每个二进制位,当两个数对应的位相同时,结果为 0,否则结果为 1。
  4. 取反运算的符号是 ∼,运算规则是:对一个数的每个二进制位进行取反操作,0 变成 1,1变成 0。。

移位运算

按照是否带符号分类可以分成算术移位和逻辑移位

逻辑移位:不考虑符号位,移位的结果只是数据所有的位数进行移位。
算术移位:算术是带有符号的数据,所以我们不能直接移动所有的位数,这可能会使得符号不正确。

  1. 左移运算的符号是 <<。左移运算时,将全部二进制位向左移动若干位,高位丢弃,低位补 0。对于左移运算,算术移位和逻辑移位是相同的。

  2. 右移运算的符号是 >>。右移运算时,将全部二进制位向右移动若干位,低位丢弃,高位的补位由算术移位或逻辑移位决定:

    • 算术右移时,高位补最高位;

    • 逻辑右移时,高位补 0。

移位运算与乘除法的关系

  1. 左移运算对应乘法运算。将一个数左移 k 位,等价于将这个数乘以 2k
  2. 将一个数(算术)右移 k 位,和将这个数除以 2k是不等价的

位运算的性质

  1. a^a = 0
  2. a^b ^a = b

在这里插入图片描述

例题

7进制

func convertToBase7(num int) string {
    if num == 0 {
        return "0"
    }
    var isMinus bool
    var ans string
    if num < 0 {
        isMinus = true
        num = -num
    }
    for num != 0 {
        ans = strconv.Itoa(num%7) + ans
        num /= 7
    }
    if isMinus {
        ans = "-" + ans
    }
    return ans
}

基本原理

0s 表示一串 0,1s 表示一串 1。

x ^ 0s = x      x & 0s = 0      x | 0s = x
x ^ 1s = ~x     x & 1s = x      x | 1s = 1s
x ^ x = 0       x & x = x       x | x = x

位 1 的个数

  1. 编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
  2. 输入必须是长度为 32 的 二进制串 。

循环每一位

func hammingWeight(num uint32) int {
	count :=0
	for i:=0;i<32;i++{
		// 此处结果为uint32要转为int
		count+= int((num>>i)&1)
	}
	return count
}

位运算性质

对于整数 n,n & (n−1) 的结果为将 n 的二进制表示的最后一个 1 变成 0。

func hammingWeight(num uint32) int {
	count :=0
	for num!=0{
		num=num&(num-1)
		count++
	}
	return count
}

只出现一次的数字 II

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

菜鸡的做法(我的做法):

func singleNumber(nums []int) int {
	counter:=make(map[int]int,len(nums))
	for _,val:=range nums{
		counter[val]++
	}
	for k,v :=range counter{
		if v==1{
			return k
		}
	}
	return -1
} 

大佬的做法:

使用变量ones 和twos 分别存储出现一次的元素的异或值以及出现两次的元素的异或值。考虑只有一个元素的情况,假设该元素为num,当该元素出现 0 次到 3 次时,ones 和 twos 的值变化如下:

出现 0 次时(即初始状态),ones=0,twos=0;

出现 1 次时,ones=num,twos=0;

出现 2 次时,ones=0,twos=num;

出现 3 次时,ones=0,twos=0,和出现 0 次时的值相同。

func singleNumber(nums []int) int {
	ones,twos:=0,0
	for _,val :=range nums{
		ones = ones^val & (^twos)
		twos = twos^val & (^ones)
	}
	return ones
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Generalzy

倍感荣幸文章对您有帮助

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值