5.23 位运算

This commit is contained in:
zhangsan 2025-05-23 09:17:44 +08:00
parent 71799259b9
commit 0760f5b78c
7 changed files with 267 additions and 1 deletions

View File

@ -0,0 +1,35 @@
package bitwise;
/**
* 题目 67. 二进制求和 (addBinary)
* 描述给你两个二进制字符串 a b 以二进制字符串的形式返回它们的和
*
* 示例 1
输入:a = "11", b = "1"
输出"100"
* 链接https://leetcode.cn/problems/add-binary/
*/
public class AddBinary {
public String addBinary(String a, String b) {
int len1=a.length(),len2=b.length();
int done=0;
int carry=0;
int cura,curb;
StringBuilder sb=new StringBuilder();
while (done<Math.max(len1,len2)){
if(done<len1)
cura=a.charAt(len1-1-done)-'0';
else cura=0;
if(done<len2)
curb=b.charAt(len2-1-done)-'0';
else curb=0;
int cur=(cura+curb+carry)%2;
carry=(cura+curb+carry)/2;
sb.insert(0,cur);
done++;
}
if(carry==1)sb.insert(0,'1');
return sb.toString();
}
}

View File

@ -0,0 +1,24 @@
package bitwise;
/**
* 题目 191. 位1的个数 (HammingWeight)
* 描述给定一个正整数 n编写一个函数获取一个正整数的二进制形式并返回其二进制表达式中 设置位 的个数也被称为汉明重量
*
*
* 示例 1
输入n = 11
输出3
解释输入的二进制串 1011 共有 3 个设置位
* 链接https://leetcode.cn/problems/number-of-1-bits/
*/
public class HammingWeight {
public int hammingWeight(int n) {
int ret = 0;
for (int i = 0; i < 32; i++) {
if ((n & (1 << i)) != 0) {
ret++;
}
}
return ret;
}
}

View File

@ -0,0 +1,37 @@
package bitwise;
/**
* 题目 201. 数字范围按位与 (rangeBitwiseAnd)
* 描述给你两个整数 left right 表示区间 [left, right] 返回此区间内所有数字 按位与 的结果包含 left right 端点
*
* 你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题
*
示例 1
输入left = 5, right = 7
输出4
* 链接https://leetcode.cn/problems/bitwise-and-of-numbers-range/
*/
//没看懂题目
public class RangeBitwiseAnd {
/**
* 经典做法右移对齐再左移复原
* shift = 0
* 如果 left < right说明它们在某个更低的位上 0~n-1 是不同的需要把它们统一右移一位并把 shift++
* 重复步骤 2直到 left == right此时这个相等的值就是它们的公共前缀所有后面变动的位都被右移丢弃了
* 最后把 left 左移 shift 还原回原来数值的高位部分即可
* @param left
* @param right
* @return
*/
public int rangeBitwiseAnd(int left, int right) {
int shift = 0;
// 一直右移直到 left right 对齐到公共前缀
while (left < right) {
left >>= 1;
right >>= 1;
shift++;
}
// 把对齐后的公共前缀左移回去低位补 0
return left << shift;
}
}

View File

@ -0,0 +1,45 @@
package bitwise;
/**
* 题目 190. 颠倒二进制位 (addBinary)
* 描述颠倒给定的 32 位无符号整数的二进制位
*
* 提示
*
* 请注意在某些语言 Java没有无符号整数类型在这种情况下输入和输出都将被指定为有符号整数类型并且不应影响您的实现因为无论整数是有符号的还是无符号的其内部的二进制表示形式都是相同的
* Java 编译器使用二进制补码记法来表示有符号整数因此 示例 2 输入表示有符号整数 -3输出表示有符号整数 -1073741825
*
* 示例 1
输入n = 00000010100101000001111010011100
输出964176192 (00111001011110000010100101000000)
解释输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596
因此返回 964176192其二进制表示形式为 00111001011110000010100101000000
* 链接https://leetcode.cn/problems/reverse-bits/
*/
public class ReverseBits {
/**
* 逐位提取拼装
*
* 初始化一个结果变量 res = 0
* 对于原数 n 的每一位 32 从最低位到最高位做
* 先把 res 左移 1 为下一个要写入的位置腾出最低位
* n 中取出当前的最低位 n & 1把它或进 res
* 再把 n 无符号右移 1 >>>= 1为下一轮提取准备
* 重复 32 最后 res 就是把原来 n 的二进制倒过来的结果
* @param n
* @return
*/
public int reverseBits(int n) {
int res = 0;
// 共处理 32
for (int i = 0; i < 32; i++) {
// 先把 res 左移为下一位腾出最低位
res <<= 1;
// 取出 n 的最低位或进 res 的最低位
res |= (n & 1);
// 无符号右移 n推动下一位到最低位
n >>>= 1;
}
return res;
}
}

View File

@ -1,4 +1,4 @@
package trick; package bitwise;
import java.util.HashSet; import java.util.HashSet;

View File

@ -0,0 +1,50 @@
package bitwise;
/**
* 题目 137. 只出现一次的数字 II (singleNumber)
* 描述给你一个整数数组 nums 除某个元素仅出现 一次 其余每个元素都恰出现 三次 请你找出并返回那个只出现了一次的元素
*
* 你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题
*
示例 1
输入nums = [2,2,3,2]
输出3
* 链接https://leetcode.cn/problems/single-number-ii/
*/
//不会
public class SingleNumber2 {
/**
* 除了那唯一一个只出现一次的数数组里其它每个数都出现了三次把它们在二进制层面叠加起来出现三次的数的每一位0/1
* 在总和里都会是 0×3=0 1×3=3而那个只出现一次的数的每一位则只加 0×1=0 1×1=1于是
*
* 统计所有数在第 i 位上 1 的个数记作 count[i]
* count[i] % 3 要么是 0说明那一位上只来自出现三次部分要么是 1说明唯一数在这一位上是 1
* 把所有 count[i] % 3 按位重组就得到了那个只出现一次的整数的二进制形式
* @param nums
* @return
*/
public int singleNumber(int[] nums) {
// 用于统计 nums 中每一位上 1 出现的次数
int[] bitCount = new int[32];
// 累加所有数字的二进制位
for (int num : nums) {
for (int i = 0; i < 32; i++) {
// 提取 num 的第 i (num >>> i) & 1
bitCount[i] += (num >>> i) & 1;
}
}
// 重组只出现一次的那个数
int result = 0;
for (int i = 0; i < 32; i++) {
// 如果 bitCount[i] % 3 == 1说明唯一数的第 i 位为 1
if (bitCount[i] % 3 != 0) {
// 将第 i 位置为 1
result |= (1 << i);
}
}
return result;
}
}

View File

@ -0,0 +1,75 @@
package heap;
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
/**
* 题目 373. 查找和最小的 K 对数字 (kSmallestPairs)
* 描述给定两个以 非递减顺序排列 的整数数组 nums1 nums2 , 以及一个整数 k
* 定义一对值 (u,v)其中第一个元素来自 nums1第二个元素来自 nums2
* 请找到和最小的 k 个数对 (u1,v1), (u2,v2) ... (uk,vk)
*
* 示例 1
输入: nums1 = [1,7,11], nums2 = [2,4,6], k = 3
输出: [1,2],[1,4],[1,6]
解释: 返回序列中的前 3 对数
[1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]
* 链接https://leetcode.cn/problems/find-k-pairs-with-smallest-sums/
*/
//有思路 但是超出时间限制需要优化
public class KSmallestPairs {
/**
* 可以把所有可能的组合看作一个虚拟的二维矩阵
*
* 行代表 nums1[i]
* 列代表 nums2[j]
* 每个点 (i,j) 对应的值是 nums1[i] + nums2[j]
* 这个矩阵的特点是从左到右从上到下递增因为两个数组都升序
*
* 我们不需要全部生成矩阵而是只维护当前最小的候选对类似 Dijkstra 算法或 BFS
*
* 初始时把前 k 行的第一列 (nums1[i], nums2[0])放入堆中因为后面的列比前面的大我们先选列头
* 每次弹出堆顶最小的和把它加入结果列表
* 如果该对还有下一列 (i, j+1) 存在就把 (i, j+1) 加入堆作为下一个候选
* 这样我们始终只关注当前最小的组合最多进行 k 次堆操作高效
* @param nums1
* @param nums2
* @param k
* @return
*/
public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
List<List<Integer>> res = new ArrayList<>();
// 边界处理任一数组为空 k==0直接返回空结果
if (nums1.length == 0 || nums2.length == 0 || k == 0) return res;
// 小顶堆存储的是每对数的索引 [i, j]按对应的和升序排列
PriorityQueue<int[]> minHeap = new PriorityQueue<>(
(a, b) -> nums1[a[0]] + nums2[a[1]] - nums1[b[0]] - nums2[b[1]]
);
// 初始化 nums1 的前 k 个元素与 nums2[0] 组成的数对放入堆中
// 因为只推进列所以只需要每一行的第一个元素
for (int i = 0; i < Math.min(nums1.length, k); i++) {
minHeap.offer(new int[]{i, 0}); // i 表示 nums1 的索引j=0 是固定的起点
}
// 取出堆顶最小的数对直到取满 k 个或者堆空为止
while (k-- > 0 && !minHeap.isEmpty()) {
int[] pos = minHeap.poll(); // 当前最小的数对在 nums1[pos[0]], nums2[pos[1]]
int i = pos[0], j = pos[1];
// 把数对值加入结果集
res.add(List.of(nums1[i], nums2[j]));
// 如果当前数对还有下一列就推进下一列仍然在第 i
if (j + 1 < nums2.length) {
minHeap.offer(new int[]{i, j + 1});
}
}
return res;
}
}