训练计划 VI

标签: 位运算 数组

难度: Medium

教学过程中,教练示范一次,学员跟做三次。该过程被混乱剪辑后,记录于数组 actions,其中 actions[i] 表示做出该动作的人员编号。请返回教练的编号。

示例 1:

输入:actions = [5, 7, 5, 5]
输出:7

示例 2:

输入:actions = [12, 1, 6, 12, 6, 12, 6]
输出:1

提示:

  • 1 <= actions.length <= 10000
  • 1 <= actions[i] < 2^31

Submission

运行时间: 240 ms

内存: 15.7 MB

class Solution:
    def trainingPlan(self, actions: List[int]) -> int:
        ones,twos = 0,0
        for action in actions:
            ones = ones^action&~twos
            twos = twos^action&~ones 
        return ones

Explain

题解利用位操作来统计每个人员编号出现的次数。通过两个变量`ones`和`twos`,分别记录每个位出现1次和2次的情况。代码通过遍历`actions`数组,对每一个元素进行位操作更新`ones`和`twos`。整体思路是使用位运算模拟三进制计数,其中`ones`表示出现一次,`twos`表示出现两次,最终任何出现三次的位会被重置为0。因此,只出现一次的教练编号将保留在`ones`中。

时间复杂度: O(n)

空间复杂度: O(1)


class Solution:
    def trainingPlan(self, actions: List[int]) -> int:
        ones, twos = 0, 0  # 初始化两个计数器,ones和twos
        for action in actions:
            ones = ones ^ action & ~twos  # 更新ones,加入新的action,排除twos中已有的
            twos = twos ^ action & ~ones  # 更新twos,加入新的action,排除ones中更新后的
        return ones  # 返回只出现一次的教练编号

Explore

位操作在处理频率统计时提供了一种更为高效和节省空间的方式。在该题目中,每个人的编号可能重复出现多次,使用位操作可以有效地模拟三进制计数(出现1次、2次、重置为0)。通过位操作,我们可以仅使用固定的几个整数变量(在本题中是`ones`和`twos`)来记录状态,相比使用哈希表,这种方法大幅减少内存消耗,并可能在某些情况下提高计算速度。此外,位操作直接在底层操作二进制位,提高了处理效率。

在这种方法中,`ones`和`twos`变量用于跟踪每个数字出现的次数。具体来说,`ones`记录了某个数字出现一次的位,而`twos`记录了某个数字出现两次的位。逻辑如下: 1. 当一个新的数字出现时,我们首先检查它是否已经在`ones`中记录过(即之前出现过一次),如果是,我们将其加入到`twos`中(表示现在出现了两次)。 2. 如果一个数字已经在`twos`中(即之前出现过两次),那么再次出现将使该数字出现次数达到三次,根据题目要求,我们需要将其计数重置为0,即从`ones`和`twos`中移除。 3. 使用位操作,我们可以有效地同时更新`ones`和`twos`,确保每个数字的出现次数能够准确计数。当循环结束后,`ones`中存储的即为只出现一次的教练编号。

`~twos`是对`twos`变量进行位取反操作,这意味着它将`twos`中所有的1变为0,所有的0变为1。在表达式`ones = ones ^ action & ~twos`中,`~twos`起到了一个过滤器的作用,确保只有当某个数字没有在`twos`中记录(即没有出现过两次)时,这个数字才会被考虑加入到`ones`中。这是为了保证当一个数字出现第三次时,它能够从`ones`和`twos`中正确地被清除,避免错误地统计出现次数。