Постановка задачи

Дан целочисленный массив nums, в котором каждый элемент появляется три раза, кроме одного, который появляется ровно один раз. Найдите отдельный элемент и верните его.

Вы должны реализовать решение с линейной сложностью времени выполнения и использовать только постоянное дополнительное пространство.

Постановка задачи взята с: https://leetcode.com/problems/single-number-ii.

Пример 1:

Input: nums = [2, 2, 3, 2] 
Output: 3

Пример 2:

Input: nums = [0, 1, 0, 1, 0, 1, 99] 
Output: 99

Ограничения:

- 1 <= nums.length <= 3 * 10^4 
- -2^31 <= nums[i] <= 2^31 
- 1 - Each element in nums appears exactly **three times** except for one element which appears **once**.

Объяснение

Решение грубой силы

Подход грубой силы состоит в том, чтобы запустить цикл дважды, а также сохранить количество каждого уникального элемента. Затем мы перебираем хэш и возвращаем элемент, который появился только один раз. Временная сложность будет O(N²), а пространственная сложность будет O(N).

Фрагмент C++ приведенной выше логики будет таким:

int singleNumber(vector<int>& nums) {
    map<int, int> m;

    for(int i = 0; i < nums.size(); i++) {
        m[nums[i]]++;
    }

    for(auto const & [key, value]: m) {
        if(value == 1) {
            return key;
        }
    }

    return -1;

}

Мы можем использовать сортировку и сделать это за O(N(log(N))).

XOR-операторы

Определить число, которое появилось только один раз, в то время как другие элементы встречались дважды, было легко с помощью оператора XOR (^). Мы можем обратиться к решению этой проблемы здесь.

В этом случае элементы массива появляются трижды, кроме одного. Одного оператора XOR будет недостаточно для идентификации одного номера. Мы сосредоточимся на использовании двух переменных и применим к ним оператор XOR. Назовем переменную как единицы и двойки.

ones — эта переменная будет содержать XOR всех элементов, которые появились только один раз. twos — эта переменная будет содержать XOR всех элементов, которые появились дважды.

В любой момент времени -

  1. Появляется новое число, которое подвергается операции XOR с переменной единиц. Он определяет первое появление числа.
  2. Число повторяется, оно удаляется из переменной единиц и подвергается операции XOR с двойками.
  3. Число появляется трижды, оно удаляется как из единиц, так и из двоек.

Окончательный ответ, который нам нужен, — это значение, присутствующее в переменной one.

Сначала проверим алгоритм:

- set ones = 0, twos = 0
  initialize common_bit_mask

- loop for i = 0; i < nums.size(); i++
  // if the number appears for the first time ones & nums[i] is 0,
  // so twos does not get any bit from nums[i]
  - twos = twos | (ones & nums[i])

  // Here the ones is set XORed with nums[i],
  // so now ones variable get the bit representation of nums[i]
  - ones = ones ^ nums[i]

  // Now if the number appeared thrice both the ones and twos
  // variable has the bit representation of nums[i].
  // We create a negate of these set bits and remove them from the
  // ones and twos variable in next steps.
  - common_bit_mask = ~(ones & twos)

  // remove the third occurrence of the number from ones variable
  - ones &= common_bit_mask

  // remove the third occurrence of the number from twos variable
  - twos &= common_bit_mask

- return ones

Временная сложность описанного выше подхода составляет O(N), а пространственная сложность — O(1). Давайте проверим наши решения на C++, Golang и Javascript.

С++ решение

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ones = 0, twos = 0;
        int common_bit_mask;

        for(int i = 0; i < nums.size(); i++) {
            twos |= (ones & nums[i]);
            ones ^= nums[i];

            common_bit_mask = ~(ones & twos);

            ones &= common_bit_mask;

            twos &= common_bit_mask;
        }

        return ones;
    }
};

Голанг решение

func singleNumber(nums []int) int {
    ones, twos, common_bit_mask := 0, 0, 0

    for i := 0; i < len(nums); i++ {
        twos = twos | (ones & nums[i])
        ones ^= nums[i]

        common_bit_mask = ^(ones & twos)
        ones &= common_bit_mask
        twos &= common_bit_mask
    }

    return ones
}

Javascript-решение

var singleNumber = function(nums) {
    let ones = 0, twos = 0, common_bit_mask = 0;

    for(let i = 0; i < nums.length; i++) {
        twos |= (ones & nums[i]);
        ones ^= nums[i];

        common_bit_mask = ~(ones & twos);
        ones &= common_bit_mask;
        twos &= common_bit_mask;
    }

    return ones;
};

Давайте запустим наш алгоритм в пробном режиме, чтобы увидеть, как работает решение.

Input: nums = [2, 2, 3, 2]

Step 1: ones = 0, twos = 0
        common_bit_mask

Step 2: loop for i = 0; i < nums.size()
        0 < 4
        true

        twos |= (ones & nums[i])
              = twos | (ones & nums[0])
              = 0 | (0 & 2)
              = 0 | 0
              = 0

        ones ^= nums[i]
              = ones ^ nums[0]
              = 0 ^ 2
              = 2

        common_bit_mask = ~(ones & twos)
                        = ~(0 & 0)
                        = -1

        ones &= common_bit_mask
              = ones & common_bit_mask
              = 2 & -1
              = 2

        twos &= common_bit_mask
              = twos & common_bit_mask
              = 0 & -1
              = 0

        i++
        i = 1

Step 3: i < nums.size()
        1 < 4
        true

        twos |= (ones & nums[i])
              = twos | (ones & nums[1])
              = 0 | (2 & 2)
              = 0 | 2
              = 2

        ones ^= nums[i]
              = ones ^ nums[1]
              = 2 ^ 2
              = 0

        common_bit_mask = ~(ones & twos)
                        = ~(0 & 2)
                        = ~(2)
                        = -1

        ones &= common_bit_mask
              = ones & common_bit_mask
              = 0 & -1
              = 0

        twos &= common_bit_mask
              = twos & common_bit_mask
              = 2 & -1
              = 2

        i++
        i = 3

Step 4: i < nums.size()
        2 < 4
        true

        twos |= (ones & nums[i])
              = twos | (ones & nums[2])
              = 2 | (0 & nums[2])
              = 2 | (0 & 3)
              = 2 | 0
              = 2

        ones ^= nums[i]
              = ones ^ nums[2]
              = 0 ^ 3
              = 3

        common_bit_mask = ~(ones & twos)
                        = ~(3 & 2)
                        = ~(2)
                        = -3

        ones &= common_bit_mask
              = ones & common_bit_mask
              = 3 & -3
              = 1

        twos &= common_bit_mask
              = twos & common_bit_mask
              = 2 & -3
              = 0

        i++
        i = 3

Step 5: i < nums.size()
        3 < 4
        true

        twos |= (ones & nums[i])
              = 0 | (1 & nums[3])
              = 0 | (1 & 2)
              = 0 | (0)
              = 0 | 0
              = 0

        ones ^= nums[i]
              = ones ^ nums[3]
              = 1 ^ 2
              = 3

        common_bit_mask = ~(ones & twos)
                        = ~(0 & 3)
                        = ~(0)
                        = -1

        ones &= common_bit_mask
              = ones & common_bit_mask
              = 3 & -1
              = 3

        twos &= common_bit_mask
              = twos & common_bit_mask
              = 0 & -1
              = 0

        i++
        i = 4

Step 6: i < nums.size()
        4 < 4
        false

Step 7: return ones

So we return the answer as 3.

Первоначально опубликовано на https://alkeshghorpade.me.

Если этот пост был полезен, пожалуйста, несколько раз нажмите кнопку аплодисментов 👏, чтобы выразить свою поддержку автору 👇

🚀Разработчики: учитесь и развивайтесь, не отставая от того, что важно, ПРИСОЕДИНЯЙТЕСЬ К FAUN.