算法:位运算

与运算

  • 与运算常用于二进制的取位操作,其用符号&表示,相同位的两个数字都为1,则为1,若有一个不为1,则为0
00101 & 11100 = 00100
3(11) & 2(10) = 2(10)
  • 应用:任意一个数&1的结果就是取二进制的最末位,常用于判断数的奇偶,最末位为0表示该数为偶数,最末位为1表示该数为奇数(其他位都为2的正幂次方)

或运算

  • 或运算常用于二进制特定位上的无条件赋值,其用符号|表示,相同位的两个数字只要一个为1即为1
00101 | 11100 = 11101
3(11) | 2(10) = 3(11)
  • 应用:一个数|1的结果就是把二进制最末位强行变成1,如果需要把二进制最末位变为0,对这个数|1后再减1即可,其实际意义就是把这个数强行变成最接近的偶数

非运算

  • 非运算是把内存中的0和1全部取反,用符号~表示,使用非运算时要格外小心,需要注意整数类型有没有符号,如果非运算的对象是无符号整数,那么得到的值是它与该类型上界的差

异或运算

  • 异或运算,是对等长二进制模式按位执行逻辑按位异或操作,用符号^表示,如果某位不同则该位为1,否则该位为0
00101^11100=11001
3(11) ^ 2(10) = 1(01)

在这里插入图片描述

  • 异或运算的逆运算是它本身,也就是说两次异或同一个数最后结果不变,即:(a^b)^b=a

左移运算

  • 左移运算,用符号<<表示,操作a<<b就是把a转为二进制后左移b位(在后面填充b个0),实质上就是a乘以2的b次方,因为在二进制数后面添个0就等于把这个数扩大2倍
400(110010000)<<2=100(1100100)
  • 通常认为,a<<1a*2要更快,因为前者是更底层的操作,因此程序中乘以2的多少次方的操作尽量用左移位来代替

右移运算

  • 右移运算,用符号>>表示,操作a>>b就是把a转为二进制后右移b位(去掉末尾b个0),实质上就是a除以2的b次方(取整)
100(1100100)>>2=400(110010000)
  • 通常认为,a>>1a/2要更快,因为前者是更底层的操作,因此程序中除以2的多少次方的操作尽量用右移位来代替

首先了解一下&&的特性,比如有A&&B

  • 如果A为true,返回B的布尔值(继续往下执行)
  • 如果A为false,直接返回false(相当于短路)

求 1+2+…+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
示例 1:
输入: n = 3 输出: 6
示例 2:
输入: n = 9 输出: 45
限制:
1 <= n <= 10000

//以n=3为例
#include <iostream>
using namespace std;

int sumNums(int n);
int main()
{
	cout << sumNums(3) << endl;
	return 0;
}

int sumNums(int n)
{
	n && (n += sumNums(n - 1));
	cout << n << endl;
	return n;
}
//
0
1
3
6
6

注意运算符的优先级防止出错

  • 常用的运算符优先级
    在这里插入图片描述

Example1

给定一个整数,编写一个函数来判断它是否是 2 的幂次方。
示例 1:
输入: 1
输出: true
解释: 20 = 1
示例 2:
输入: 16
输出: true
解释: 24 = 16
示例 3:
输入: 218
输出: false

解题思路
  • 观察一些是2的幂的二进制数:
    在这里插入图片描述
  • 可以发现这些数,都是最高位为1,其他位为0.所以把问题转化为“判断一个数的二进制,除了最高位为1,是否还有别的1存在”
    在这里插入图片描述
  • 观察第一组数对应减1,再对两组数据求&运算
    在这里插入图片描述
  • 对于N为2的幂的数,都有N & (N - 1) = 0,所以这就是判断的
//c++
class Solution {
public:
    bool isPowerOfTwo(int n) {
        return n > 0 && ((n & (n - 1)) == 0);
    }
};

Example2

示例 1:
输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串00000000000000000000000000001011 中,共有三位为 ‘1’。
示例 2:
输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 ‘1’。
示例 3:
输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串11111111111111111111111111111101 中,共有 31 位为 ‘1’。

解题思路
  • 利用位运算的思想,构造出一个掩码来与目标值的当前位进行&运算
  • 1的二进制
    在这里插入图片描述
  • 只需要让这个掩码每次向左移动一位,然后与目标值求&,就可以判断目标值的当前位是不是1,比如21的二进制
    在这里插入图片描述
  • 每次移动掩码来和当前位进行计算
    在这里插入图片描述

Java实现

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int result = 0;
        int mask = 1;
        for(int i = 0;i < 32;i++)
        {
            if((mask & n) != 0) 
            result++;
            mask <<= 1;
        }
        return result;   
    }
}
//左移mask会改变
public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int result = 0;
        int mask = 1;
        for(int i = 0;i < 32;i++)
        {
            if((mask & n) == 1) 
            result++;
            n >>= 1;
        }
        return result;   
    }
}
//右移n
  • 优化:刚才对于判断2的幂数利用n & (n - 1)方式,去掉n的最后一个1
    在这里插入图片描述

Cpp实现

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int count = 0;
        while(n > 0)
        {
            n &= (n - 1);
            count++;
        }
        return count;
    }
};

Example3

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4

解题思路
  • 利用异或运算:通过异或运算的结合律,出现两次的元素异或后为0,再根据0和0的异或仍然为0,在去跟出现一次的数字作异或运算这样就得到只出现一次的数字了,在这里我们按照数组的顺序来进行异或运算即可
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int res = nums[0];
        for(int i = 1;i < nums.size();i++)
        {
            res ^= nums[i];
        }
        return res;
    }
};

Example3

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99

解题思路
  • [A,A,A,B,B,B,C,C,C]和[A,A,A,B,B,B,C]差了两个C即:3×(a+b+c) - (a+a+a+b+b+b+c) = 2c,也就是说,如果把原数组去重、再乘以3得到的值,刚好就是要找的元素的两倍
    在这里插入图片描述

Python实现

注:使用了额外的空间

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        return int((sum(set(nums)) * 3 - sum(nums)) / 2) 
解题思路
  • 位运算:让出现三次的元素归0(注意出现两次的元素通过异或运算可以归0)
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int res = 0;
        for (int i = 0; i < 32; ++i) {
            int number = 0;
            for (auto x : nums) {
                number += (x>>i)&1;
            }
            res |= (number % 3)<<i;
        }
        return res;
    }
};

Example4

给定一个包含 0, 1, 2, …, n 中 n 个数的序列,找出 0 … n 中没有出现在序列中的那个数。
示例 1:
输入: [3,0,1]
输出: 2
示例 2:
输入: [9,6,4,2,3,5,7,0,1]
输出: 8

解题思路
  • 利用高斯公式:首项加末项的和乘以项数除以2先确定0到n的和,之后减去给定数组的所有元素之和,即得到缺失的元素

Cpp实现

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int l = nums.size();
        int s = l * (l + 1) / 2;
        int sum = 0;
        for(int i = 0;i < l;i++)
        {
            sum += nums[i];
        }
        return s - sum;
    }
};
解题思路
  • 利用异或位运算

Java实现

class Solution {
    public int missingNumber(int[] nums) {
        int res = 0;
        for(int i = 0;i < nums.length;i++)
        {
            res ^= nums[i] ^ i;
        }
        return res ^ nums.length;

    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值