Rotate Bits Problem in Java

2025 年 5 月 6 日 | 阅读 7 分钟

位旋转问题涉及将整数的位向左或向右移动,并将溢出的位环绕到另一端。此操作在底层编程、加密和数据处理任务中至关重要。Java 提供了按位运算符,可以高效地实现有符号和无符号数字的位旋转。

示例

输入:数字 = 19 (二进制:10011),左旋 2 位。

输出:左旋结果:14

输入:数字 = 19 (二进制:10011),右旋 2 位。

输出:右旋结果:28

解释

在示例中,将 19 (二进制 10011) 左旋 2 位得到 14 (二进制 01110),因为溢出的位 11 环绕到了末尾。类似地,将 19 右旋 2 位得到 28 (二进制 11100),因为溢出的位 11 移动到了左侧。

按位旋转方法

步骤 1:理解输入:数字:需要旋转位的整数(例如,19,二进制为 10011)。

位长度:旋转过程中要考虑的位数(例如,为简化起见,为 5 位)。

旋转量:移动位的位数(例如,2 位)。

步骤 2:左旋:左移:将数字的位向左移动指定的旋转量(number << rotation)。

示例:将 10011 左移 2 位得到 1001100。

提取溢出位:通过将原始数字右移(number >>> (bit_length - rotation))来计算在左移过程中“脱离”左侧的位。

示例:将 10011 右移 3 位得到 00010。

步骤 2.1:合并结果:使用按位 OR 运算符将左移结果与溢出位合并。

示例:将 1001100 与 00010 合并得到 1001100。

限制到位长度:如果结果超过了位长度,则使用 (1 << bit_length) - 1 进行掩码,以确保只保留所需的位。

步骤 3:右旋:右移:将数字的位向右移动指定的旋转量(number >>> rotation)。

示例:将 10011 右移 2 位得到 00100。

提取溢出位:通过将原始数字左移(number << (bit_length - rotation))来计算在右移过程中“脱离”右侧的位。

示例:将 10011 左移 3 位得到 1000000。

步骤 4:合并结果:使用按位 OR 运算符将右移结果与溢出位合并。

示例:将 00100 与 1000000 合并得到 1000011。

限制到位长度:对结果进行掩码,以确保只保留所需的位数。

步骤 4.1:验证结果:执行位旋转后,通过将旋转后的值与预期输出进行比较来验证结果非常重要。可以通过将二进制结果转换回十进制并确保其与预期结果匹配来完成。使用各种输入进行测试将有助于确保算法的准确性和正确性。

步骤 5:输出结果:执行左旋和右旋后,显示两个操作的最终结果。如果需要,通过掩码来确保结果在指定的位长度内。最终输出将显示旋转后的整数值,演示了位向左和向右移动的效果。

文件名:RotateBits.java

输出

 
Rotate Left Result: 14
Rotate Right Result: 28   

复杂度分析

时间复杂度

位旋转算法的时间复杂度为 O(1)。这是因为每次旋转操作都涉及恒定的按位操作(移位和掩码),这些操作的时间与数字的大小或旋转量无关。因此,该算法以连续时间执行。

空间复杂度

位旋转算法的空间复杂度为 O(1)。该算法仅需要固定的空间来存储输入数字、位长度和旋转过程中的中间结果。它不使用任何额外的数据结构或随输入大小增长的内存。

使用模运算的有效旋转方法

算法

位长度:旋转要考虑的位数(例如,5)。

旋转量:移动位的位数(例如,2)。

步骤 1:理解问题

数字:需要旋转位的整数(例如,19,二进制 10011)。

位长度:旋转要考虑的位数(例如,5)。

旋转量:移动位的位数(例如,2)。

它包括

将数字的位向左或向右移动指定的旋转量。

将任何溢出的位环绕到另一端。

步骤 2:处理旋转量:旋转量可能超过位长度。例如,将 5 位旋转 7 位相当于旋转 7 % 5 = 2 位。

有效旋转 = rotation % bitlength。

使用模运算符计算有效旋转。

有效旋转 = rotation % bitlength。

步骤 2.1:确定旋转方向:计算出有效旋转后,根据要求决定执行左旋还是右旋。每个方向都涉及特定的按位操作。

左旋:将位向左移位,并将溢出位环绕到最右边的位置。

右旋:将位向右移位,并将溢出位环绕到最左边的位置。

步骤 3:左旋

左移:将数字的位向左移动有效旋转量。使用左移运算符(<<)。

例如,将 19 (10011) 左移 2 位得到 1001100。

提取溢出位:溢出位是在移位过程中从最左边移出的位。通过将原始数字右移(bitLength - effective Rotation)来计算这些位。使用无符号右移运算符(>>>)。

例如,将 19 (10011) 右移 3 位得到 00010。

环绕:使用按位 OR 运算符(|)将左移的数字和溢出位合并。

例如,合并 1001100 和 00010 得到 1001110。

步骤 3.1:掩码以适应位长度:应用按位掩码以确保结果适合指定的位长度。掩码为 (1 << bitLength) - 1,它创建一个只有所需位设置为 1 的二进制数。例如,对于 5 位,掩码为 11111。应用它可确保只保留前 5 位。

步骤 4:右旋

右移:将数字的位向右移动有效旋转量。使用无符号右移运算符(>>>)。例如,将 19 (10011) 右移 2 位得到 00100。

提取溢出位:溢出位是在移位过程中从最右边移出的位。通过将原始数字左移(bit Length - effective Rotation)来计算这些位。使用左移运算符(<<)。例如,将 19 (10011) 左移 3 位得到 1000000。

环绕:使用按位 OR 运算符(|)将右移的数字和溢出位合并。例如,合并 00100 和 1000000 得到 1000011。

步骤 4.1:掩码以适应位长度:应用按位掩码 (1 << bitLength) - 1 将结果保持在指定的位长度内。

步骤 5:验证结果:将最终的二进制结果转换回十进制以确认正确性。使用不同的输入值进行测试,以确保实现一致地工作。

步骤 6:处理边缘情况:处理特殊情况以确保鲁棒性

零旋转:如果旋转量为 0,则数字保持不变。对于这种情况,跳过旋转逻辑。

全周期旋转:当旋转量是位长度的倍数时(例如,将 5 位旋转 5 位或 10 位),数字保持不变。避免不必要的计算。

输入验证:确保输入有效,例如检查位长度是否与数字的二进制表示匹配,或者旋转量是否为非负数。

文件名:RotateBitsAlt.java

输出

 
Rotate Left Result: 14
Rotate Right Result: 28   

复杂度分析

时间复杂度

位旋转算法的时间复杂度为 O(1),因为无论输入大小如何,它都执行固定数量的按位操作——移位、掩码和合并。这些操作以恒定时间执行,使得该算法高效且独立于要旋转的位数。

空间复杂度

位旋转算法的空间复杂度为 O(1)。它使用恒定的内存量来存储输入数字、位长度、旋转值和中间结果。不需要额外的数据结构,因为所有操作都使用按位运算符就地执行,确保了最小的内存使用。