LeetCode刷题——剑指offer位运算题目汇总(包含快速乘、快速幂、二进制加法)


剑指 Offer II 001. 整数除法

题目
给定两个整数 a 和 b ,求它们的除法的商 a/b ,要求不得使用乘号 ‘*’、除号 ‘/’ 以及求余符号 ‘%’ 。

注意:

整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231−1]。本题中,如果除法结果溢出,则返回 231 − 1

示例 1:

输入:a = 15, b = 2 输出:7
解释:15/2 = truncate(7.5) = 7

示例 2:

输入:a = 7, b = -3 输出:-2
解释:7/-3 truncate(-2.33333…) = -2

示例 3:

输入:a = 0, b = 1 输出:0

示例 4:

输入:a = 1, b = 1 输出:1

提示:

-231 <= a, b <= 231 - 1 b != 0

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/xoh6Oh
分析
由于题目中不允许使用除法,一般像这种问题我们要考虑使用二进制,假设我们有a=15, b=2,我们可以用减法来统计

a-b
减0次15
减1次13
减2次11
减3次9
减4次7
减5次5
减6次3
减7次1
减到第7次a已经小于b了,此时不能再减,所以答案是7

但是这样会超时,我们接下来进行优化

a - n * bans
减0次150
减7次15 - 7 * 27
这样能够马上得出答案,但是题目中要求不能使用乘号,在二进制中左移可以达到乘二的效果
na-b*(1<<n)
---
相当于减4次215-2*(1<<2)=7
相当于减2次17-2*(1<<1)=3
相当于减1次03-2*(1<<0)=1

代码

class Solution {
public:
    int divide(int a, int b) {
        if (a == INT_MIN && b == -1) return INT_MAX;

        int sign = (a > 0) ^ (b > 0) ? -1 : 1;

        unsigned int ua = abs(a);
        unsigned int ub = abs(b);
        unsigned int res = 0;
        for (int i = 31; i >= 0; i--) {
            if ((ua >> i) >= ub) {
                ua = ua - (ub << i);
                res += 1 << i;
            }
        }
        return sign == 1 ? res : -res;
    }
};

剑指 Offer II 002. 二进制加法

给定两个 01 字符串 a 和 b ,请计算它们的和,并以二进制字符串的形式输出。

输入为 非空 字符串且只包含数字 1 和 0。

示例 1:

输入: a = “11”, b = “10” 输出: “101”

示例 2:

输入: a = “1010”, b = “1011” 输出: “10101”

提示:

每个字符串仅由字符 ‘0’ 或 ‘1’ 组成。 1 <= a.length, b.length <= 10^4 字符串如果不是 “0” ,就都不含前导零。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/JFETK5
代码

class Solution {
public:
    string addBinary(string a, string b) {
        if(a.size() < b.size()){
            string tmp = a;
            a = b;
            b = tmp;
        }
        int cur=0;
        int al=a.size()-1, bl=b.size()-1;
        while(bl >= 0){
            int tmp = cur + a[al]-'0' + b[bl]-'0';
            a[al] = tmp % 2 + '0';
            cur = tmp / 2;
            al--;
            bl--;
        }
        while(cur && al >= 0){
            int tmp = cur + a[al]-'0';
            a[al] = tmp % 2 + '0';
            cur= tmp / 2;
            al--;
        }
        if(cur){
            return '1' + a;
        }
        return a;
    }
};

剑指 Offer II 003. 前 n 个数字二进制中 1 的个数

给定一个非负整数 n ,请计算 0 到 n 之间的每个数字的二进制表示中 1 的个数,并输出一个数组。

示例 1:

输入: n = 2
输出: [0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10

示例 2:

输入: n = 5
输出: [0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101

说明 :

0 <= n <= 105

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/w3tCBm
代码

class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> ans;
        for(int i=0; i <= n; i++){
            int t=i, cnt=0;
            while(t){
                t &= (t-1);
                cnt++;
            }
            ans.push_back(cnt);
        }
        return ans;
    }
};

剑指 Offer II 004. 只出现一次的数字

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

示例 1:

输入:nums = [2,2,3,2]
输出:3

示例 2:

输入:nums = [0,1,0,1,0,1,100]
输出:100

提示:

1 <= nums.length <= 3 * 104
-231 <= nums[i] <= 231 - 1 nums 中,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/WGki4K
分析
方法1:我们可以遍历每一位1的个数,如果二进制位之和是3的倍数说明出现了三次,即我们要找的答案中这一位为0,否则答案这一位为1
方法2:通过方法1我们可以得出状态有三种:即对3取余,余数为0,1, 2,我们可以用两位二进制表示这三种状态,这里我们用a和b表示两位状态,x为当前的数

a_ib_ix_i新的a_i新的b_i
00000
00101
01001
01110
10010
10000
根据真值表我们可以写出逻辑表达式
$$a=ab’x’+a’bx
\b=a’b’x+a’bx’=a’(b\bigoplus a)
$$
由于数字出现的次数只能是1或3,导致状态只能是0或1,并且状态为1时该位数位在答案中,那么我们可以直接返回b作为答案
方法1代码
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ans=0;
        for(int i=0; i < 32; i++){
            int cnt=0;
            for(auto & n:nums){
                cnt += (n>>i & 1);
            }
            if(cnt % 3){
                ans |= (1<<i);
            }
        }
        return ans;
    }
};

方法2代码

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int a=0, b=0;
        for(auto & n:nums){
            int ta, tb;
            ta = (~a & b & n)|(a & ~b & ~n);
            tb = ~a & (b ^ n);
            a = ta;
            b= tb;
        }
        return b;
    }
};

剑指 Offer II 005. 单词长度的最大乘积

给定一个字符串数组 words,请计算当两个字符串 words[i] 和 words[j] 不包含相同字符时,它们长度的乘积的最大值。假设字符串中只包含英语的小写字母。如果没有不包含相同字符的一对字符串,返回 0。

示例 1:

输入: words = [“abcw”,“baz”,“foo”,“bar”,“fxyz”,“abcdef”]
输出: 16
解释: 这两个单词为 “abcw”, “fxyz”。它们不包含相同字符,且长度的乘积最大。

示例 2:

输入: words = [“a”,“ab”,“abc”,“d”,“cd”,“bcd”,“abcd”]
输出: 4
解释: 这两个单词为 “ab”, “cd”。

示例 3:

输入: words = [“a”,“aa”,“aaa”,“aaaa”]
输出: 0
解释: 不存在这样的两个单词。

提示:

2 <= words.length <= 1000
1 <= words[i].length <= 1000
words[i]仅包含小写字母

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/aseY1I
分析
我们可以将字符串中含有的字母用二进制表示,如果两个字符串存在相同的字母那么他们做与运算结果不为0,如果不存在相同字母与运算结果为0。

还可以通过对字符串从大到小排序进行优化,这样一旦找出符合就可以返回,虽然时间复杂度不会变,但是实际实际会减少。
代码

class Solution {
public:
    int maxProduct(vector<string>& words) {
        vector<int> v;
        for(auto & w:words){
            int t=0;
            for(int i=0; i < w.size(); i++){
                t |= (1<<(w[i]-'a'));
            }
            v.push_back(t);
        }
        int ans = 0;
        for(int i=0; i < words.size()-1; i++){
            for(int j=i+1; j < words.size(); j++){
                if((v[i] & v[j]) == 0){
                    if(ans < words[i].size() * words[j].size()){
                        ans = words[i].size() * words[j].size();
                    }
                }
            }
        }
        return ans;
    }
};

剑指 Offer 15. 二进制中1的个数

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

提示:

请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
在 Java 中,编译器使用 二进制补码 记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。

示例 1:

输入:n = 11 (控制台输入 00000000000000000000000000001011)
输出:3 解释:输入的二进制串
00000000000000000000000000001011 中,共有三位为 ‘1’。

示例 2:

输入:n = 128 (控制台输入 00000000000000000000000010000000)
输出:1 解释:输入的二进制串
00000000000000000000000010000000 中,共有一位为 ‘1’。

示例 3:

输入:n = 4294967293 (控制台输入 11111111111111111111111111111101,部分语言中 n =-3)
输出:31 解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 ‘1’。

提示:

输入必须是长度为 32 的 二进制串 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-jin-zhi-zhong-1de-ge-shu-lcof
代码

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

剑指 Offer 16. 数值的整数次方(快速幂)

题目
实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。

示例 1:

输入:x = 2.00000, n = 10
输出:1024.00000

示例 2:

输入:x = 2.10000, n = 3
输出:9.26100

示例 3:

输入:x = 2.00000, n = -2
输出:0.25000 解释:2-2 = 1/22 = 1/4 = 0.25

提示:

-100.0 < x < 100.0
-231 <= n <= 231-1
-104 <= xn <= 104

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zhi-de-zheng-shu-ci-fang-lcof
代码

class Solution {
public:
    double myPow(double x, int n) {
        if(n == 0 || x == 1){
            return 1;
        }
        long long p = n;
        if(n < 0){
            x = 1.0 / x;
            p = -p;
        }
        double ans=1;
        while(p){
            if(p & 1){
                ans *= x;
            }
            x *= x;
            p >>= 1;
        }
        return ans;
    }
};

剑指 Offer 64. 求1+2+…+n(快速乘)

题目
求 1+2+…+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

示例 1:

输入: n = 3 输出: 6

示例 2:

输入: n = 9 输出: 45

限制:

1 <= n <= 10000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/qiu-12n-lcof
分析
我们都知道求和公式为n(n+1)/2,但是我们在这里不能使用乘除法,我们可以考虑使用位运算的快速乘来实现乘法,使用位运算的右移来实现除2。但是快速乘需要借助循环来实现,所以我们可以将循环拆开
由 2 13 = 8192 < 10000 < 16384 = 2 14 得 由2 ^{13}=8192<10000< 16384=2^{14}得 213=8192<10000<16384=214
最多循环14次,所以我们拆成14个条件即可
快速乘代码

#include<iostream>

using namespace std;

int main(){
	int a, b, ans=0;
	cin>>a>>b;
	while(b){
		if(b & 1){
			ans += a;
		} 
		a <<= 1;
		b >>= 1;
	}
	cout<<ans<<endl;
} 

题目代码

class Solution {
public:
    int sumNums(int n) {
        int a=n, b=n+1;
        int ans=0;
        (b & 1) && (ans += a);
        a <<= 1;
        b >>= 1;

        (b & 1) && (ans += a);
        a <<= 1;
        b >>= 1;

        (b & 1) && (ans += a);
        a <<= 1;
        b >>= 1;

        (b & 1) && (ans += a);
        a <<= 1;
        b >>= 1;

        (b & 1) && (ans += a);
        a <<= 1;
        b >>= 1;

        (b & 1) && (ans += a);
        a <<= 1;
        b >>= 1;

        (b & 1) && (ans += a);
        a <<= 1;
        b >>= 1;

        (b & 1) && (ans += a);
        a <<= 1;
        b >>= 1;

        (b & 1) && (ans += a);
        a <<= 1;
        b >>= 1;

        (b & 1) && (ans += a);
        a <<= 1;
        b >>= 1;

        (b & 1) && (ans += a);
        a <<= 1;
        b >>= 1;
        
        (b & 1) && (ans += a);
        a <<= 1;
        b >>= 1;

        (b & 1) && (ans += a);
        a <<= 1;
        b >>= 1;

        (b & 1) && (ans += a);
        a <<= 1;
        b >>= 1;
        return ans >> 1;
    }
};

剑指 Offer 65. 不用加减乘除做加法(二进制加法)

题目
写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。

示例:

输入: a = 1, b = 1
输出: 2
提示:

a, b 均可能是负数或 0 结果不会溢出 32 位整数

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/bu-yong-jia-jian-cheng-chu-zuo-jia-fa-lcof
分析
基本上就是二进制加法的思路,首先将产生的进位保存到carry,然后求进位加法(异或)的值,在代码中将这个值重新赋给了a。那么现在的问题就变成了a和进位的加法,要注意的是,此时的carry要左移一位。
代码

class Solution {
public:
    int add(int a, int b) {
        while(b){
            int carry = a & b;
            a = a ^ b;
            b = (unsigned)carry << 1;
        }
        return a;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

艾醒(AiXing-w)

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值