力扣刷题——位运算(二)

  1. 只出现一次的数字

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

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

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

思路位运算异或消除相同项
我们知道异或能够消除相同项,利用的是A ^ A = 0的性质;
另外,还有个性质是A ^ 0 = A,两条性质结合即可解决本题:
相同的出现两次的数字会相互抵消,抵消后的结果为0,
而0与任何数异或都等于它本身,所以就得到了丢失的数字

int singleNumber(int* nums, int numsSize) {
    
    
    int ret = 0;
    for (int i = 0; i < numsSize; i++) {
    
    
        ret ^= nums[i];
    } return ret;
}

.
.
.

  1. 位1的个数

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

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

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

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

思路位运算的普通移位操作
没什么花里胡哨的,极其简单的一道题,每次判断最低位是否为一,
然后不断移位就行了。

int hammingWeight(uint32_t n) {
    
    
    int cnt = 0;
    while (n) {
    
    
        if (n & 1)
        	cnt++;
        n >>= 1;
    } return cnt;
}

.
.
.

  1. 找不同

给定两个字符串 s 和 t,它们只包含小写字母。
字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。
请找出在 t 中被添加的字母

示例:
输入:
s = “abcd”
t = “abcde”
输出:e
解释:‘e’ 是那个被添加的字母。

思路1位运算异或操作寻找相同相、异项
思路参考268题,两个字符串每个字符依次异或,
那么相同的会被消去,剩下的就是多出来的字符
或者说是丢失的字符

char findTheDifference(char* s, char* t) {
    
    
    char ret = 0;
    for (size_t i = 0; s[i]; ret ^= s[i++]);
    for (size_t i = 0; t[i]; ret ^= t[i++]);
    return ret;
}

思路2字符串求和相减求差值
既然是参考268题,那么自然也可以采用这种思路
两个字符串各自求和,然后两者相减,得到的结果就是相差的字符
这里就不展开了,有兴趣的可以去试一试

.
.
.

  1. 二进制表示中质数个计算置位

给定两个整数 L 和 R ,找到闭区间 [L, R] 范围内,
计算置位位数为质数的整数个数。
(注意,计算置位代表二进制表示中1的个数。
例如 21 的二进制表示 10101 有 3 个计算置位。
还有,1 不是质数。)

示例 1:
输入: L = 6, R = 10
输出: 4
解释:
6 -> 110 (2 个计算置位,2 是质数)
7 -> 111 (3 个计算置位,3 是质数)
9 -> 1001 (2 个计算置位,2 是质数)
10-> 1010 (2 个计算置位,2 是质数)

示例 2:
输入: L = 10, R = 15
输出: 5
解释:
10 -> 1010 (2 个计算置位, 2 是质数)
11 -> 1011 (3 个计算置位, 3 是质数)
12 -> 1100 (2 个计算置位, 2 是质数)
13 -> 1101 (3 个计算置位, 3 是质数)
14 -> 1110 (3 个计算置位, 3 是质数)
15 -> 1111 (4 个计算置位, 4 不是质数)

思路硬求
分成三步:
一:计算一个数的位1的个数,即计算一个数的汉明重量
二:判断一个数是否为质数
三:for循环从L开始一直到R,计算符合条件的数字的个数

第一步可以参考191题
第二步,判断一个数是否为质数,
用一个for循环,判断从2到根号n是否能够被n整除即可
但是这里由于题目的特殊性,可以采用枚举法提前列出所有可能
——因为我们判断的是汉明重量是否为质数,
而给出的数的范围最大也就32bit,懂我的意思吗,
我们不是判断某个数是否为质数,而是某个数的汉明重量是否为质数,
所以只可能出现32以内的质数,于是我们在第二步可以列表
这样就避免了每个数字都要进一次for循环浪费时间
第三步,就是求解答案了,没啥好说的

//判断一个数是否为质数
bool isP(int num) {
    
    
    //方法1:
    //if (num < 2)return false;
    //int root = (int)sqrt((double)num);
    //for (int i = 2; i <= root; i++) {
    
    
    //    if (num % i == 0)return false;
    //}return true;

    //方法2:
    switch (num) {
    
    
    case 2:
    case 3:
    case 5:
    case 7:
    case 11:
    case 13:
    case 17:
    case 19:
    case 23:
    case 29:
    case 31:
        return true;
    default:
        return false;
    }
}

//计算一个数的汉明重量
int hammingWeight(uint32_t n) {
    
    
    int cnt = 0;
    while (n) {
    
    
        if (n & 1)
        	cnt++;
        n >>= 1;
    } return cnt;
}

//求解题目
int countPrimeSetBits(int L, int R) {
    
    
    int cnt = 0;
    for (int i = L; i <= R; i++) {
    
    
        if (isP(hammingWeight(i)))
        	cnt++;
    } return cnt;
}

.
.
.

“妙啊”

面试题 05.07. 配对交换

配对交换。编写程序,交换某个整数的奇数位和偶数位,
尽量使用较少的指令
(也就是说,位0与位1交换,位2与位3交换,以此类推)

示例1:
输入:num = 2(或者0b10)
输出 1 (或者 0b01)

示例2:
输入:num = 3
输出:3

思路分别提取奇数位和偶数位,移位后相加
由于是要交换奇数位和偶数位,
那么我们先把所有奇数位、所有偶数位提取出来,
然后所有偶数位左移、所有奇数位右移交换(你细品)
再把移位后的结果叠加,就完成了交换(女少口啊!)

于是我们分为三步走:提取奇数位、提取偶数位、叠加
和0x0101 0101 0101 0101 0101 0101 0101 0101相与可以提取偶数位
和0x1010 1010 1010 1010 1010 1010 1010 1010相与可以提取奇数位
但是这样书写实在不方便,计算器换算一下,发现它们分别是:0x55555555和0xaaaaaaaa
然后再分别向左移位和向右移位,然后叠加——叠加我们就相或就可以了

int exchangeBits(int num) {
    
    
    return ((num & 0x55555555) << 1) | ((num & 0xaaaaaaaa) >> 1);
}

.
.
.

  1. 将数字变成 0 的操作次数

给你一个非负整数 num ,请你返回将它变成 0 所需要的步数
如果当前数字是偶数,你需要把它除以 2 ;否则,减去 1 

示例 1:
输入:num = 14
输出:6
解释:
步骤 1) 14 是偶数,除以 2 得到 7 。
步骤 2) 7 是奇数,减 1 得到 6 。
步骤 3) 6 是偶数,除以 2 得到 3 。
步骤 4) 3 是奇数,减 1 得到 2 。
步骤 5) 2 是偶数,除以 2 得到 1 。
步骤 6) 1 是奇数,减 1 得到 0 。

示例 2:
输入:num = 8
输出:4
解释:
步骤 1) 8 是偶数,除以 2 得到 4 。
步骤 2) 4 是偶数,除以 2 得到 2 。
步骤 3) 2 是偶数,除以 2 得到 1 。
步骤 4) 1 是奇数,减 1 得到 0 。

示例 3:
输入:num = 123
输出:12

思路位运算——移位
算是很简单的一道题,每次判断最低位是否为1,
如果为1,需要两步操作——减一和除以二——当然这两步我们不需要真的去做
如果为0,则只需要除以二
这里我们用移位操作来取代减法和除法,
遇到1则计数值+2,遇到0则计数值+1,
同时每次循环移位一次,直到num为0

这里有个要注意的点:
当num为1时,只需要最后一步减1即可完成题目,
所以最后我们多加了1,需要减回去

int numberOfSteps(int num) {
    
    
    int ret = 0;
    while (num) {
    
    
        (num & 1) ? (ret += 2) : (ret++);
        num >>= 1;
        if (num == 0)
        	ret--;	//最后一步,把ret减回一个1
    } return ret;
}

.
.
.

  1. 颠倒二进制位

颠倒给定的 32 位无符号整数的二进制位

示例 1:
输入: 00000010100101000001111010011100
输出: 00111001011110000010100101000000
解释: 输入的二进制串00000010100101000001111010011100 表示无符号整数 43261596,因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。

示例 2:
输入:11111111111111111111111111111101
输出:10111111111111111111111111111111
解释:输入的二进制串11111111111111111111111111111101 表示无符号整数 4294967293,因此返回 3221225471 其二进制表示形式为 10111111111111111111111111111111 。

思路位运算——移位
新建一个变量ret用来返回结果,初始化为0
循环进行移位,每次num移出一位,ret移入移位
移位32次后,num的值就全部移入ret中了,则实现题目要求

这里要注意一点,32位的数,只需要移位31次就够了,
因为最开始的0本来就占了一位

所以只移位31次,但是最后一次移位后记得补上最后一位

uint32_t reverseBits(uint32_t n) {
    
    
    uint32_t ret = 0;
    for (size_t i = 0; i < 32 - 1; i++) {
    
    
        ret |= (n & 1);
        n >>= 1, ret <<= 1;
    } return ret | (n & 1);
}

.
.
.

  1. 交替位二进制数

给定一个正整数,检查他是否为交替位二进制数:
换句话说,就是他的二进制数相邻的两个位数永不相等。

示例 1:
输入: 5
输出: True
解释: 5的二进制数是: 101

示例 2:
输入: 7
输出: False
解释: 7的二进制数是: 111

示例 3:
输入: 11
输出: False
解释: 11的二进制数是: 1011

示例 4:
输入: 10
输出: True
解释: 10的二进制数是: 1010

思路1位运算——移位出来判断
新建一个check变量,
当num不为0时,每次把num的最低位取出来赋给check
然后num进行一次移位,再把当前最新的最低值和之前的check作比较
如果相同则返回false,整个遍历顺利结束,则返回true

bool hasAlternatingBits(int n) {
    
    
    while (n) {
    
    
        int check = (n & 1);
        n >>= 1;
        if (check == (n & 1))
        	return false;
    } return true;
}

思路2穷举法
因为只有32bit,所以符合条件的数其实也没有那么多个
那么实在想不出第一种思路的情况下,狗急跳墙也可以用穷举法,这里就不写代码了

猜你喜欢

转载自blog.csdn.net/weixin_44692935/article/details/108355035
今日推荐