Постановка задачи
Дан целочисленный массив nums, в котором ровно два элемента встречаются только один раз, а все остальные элементы встречаются ровно дважды. Найдите два элемента, которые встречаются только один раз. Вы можете вернуть ответ в любом порядке.
Вы должны написать алгоритм, который работает с линейной сложностью во время выполнения и использует только постоянное дополнительное пространство.
Постановка задачи взята с: https://leetcode.com/problems/single-number-iii
Пример 1:
Input: nums = [1, 2, 1, 3, 2, 5] Output: [3, 5] Explanation: [5, 3] is also a valid answer.
Пример 2:
Input: nums = [-1, 0] Output: [-1, 0]
Пример 3:
Input: nums = [0, 1] Output: [0, 1]
Ограничения:
- 2 <= nums.length <= 3 * 10^4 - 2^31 <= nums[i] <= 2^31 - 1 - Each integer in nums will appear twice, only two integers will appear once.
Объяснение
Сортировка
Сортируем элементы массива и сравниваем соседние элементы. Мы можем легко получить неповторяющийся элемент, используя описанный выше подход.
Фрагмент C++ приведенного выше подхода выглядит следующим образом:
sort(nums, nums + n); vector<int> result; for (int i = 0; i < n - 1; i = i + 2) { if (nums[i] != nums[i + 1]) { result.push_back(nums[i]); i = i - 1; } } if (result.size() == 1) result.push_back(nums[n - 1]); return result;
Временная сложность программы составляет O(nlog(n)), а пространственная сложность — O(1).
HashMap
Проблема может быть решена за O(n) с помощью HashMap. Мы запускаем цикл по массиву и подсчитываем количество вхождений каждого элемента в массиве.
Мы перебираем хэш и печатаем два числа, которые встречались только один раз.
Фрагмент C++ приведенного выше подхода выглядит следующим образом:
int n = nums.size(); vector<int> result; if(n == 0) { return result; } map<int, int> m; for(int i = 0; i < n; i++) { m[nums[i]]++; } vector<int> result; for(auto i = m.begin(); i != m.end(); i++) { if(i->second == 1) { result.push_back(i->first); } } return result;
Временная сложность программы составляет O(n), а пространственная сложность составляет O(n).
XOR-оператор
Программа может быть решена с временной сложностью O(n) и пространственной сложностью O(1) с использованием операции XOR.
Пусть a и b — элементы, встречающиеся ровно один раз в массиве nums. Сначала мы вычисляем XOR всех элементов массива.
xor = nums[0]^nums[1]^nums[2]....nums[n - 1]
Все биты, установленные в приведенной выше переменной xor, будут установлены в одном неповторяющемся элементе a или b.
Мы берем любой бит набора xor и делим элементы массива на два набора. Один набор элементов с одним и тем же установленным битом и другой с тем же самым битом, не установленным.
Мы делаем XOR для всех элементов в первом наборе, который возвращает первый неповторяющийся элемент, и делая то же самое в другом наборе, мы возвращаем второй неповторяющийся элемент.
Сначала проверим алгоритм.
- set xorResult = nums[0] a = 0, b = 0, i = 0 vector<int> result - loop for i = 1; i < nums.size(); i++ - xorResult ^= nums[i] - set setBitNo = xorResult == INT_MIN ? 0 : xorResult & ~(xorResult - 1) - loop for i = 0; i < nums.size(); i+= - if nums[i] & setBitNo - a ^= nums[i] - else - b ^= nums[i] - result.push_back(a) - result.push_back(b) - return result
Временная сложность описанного выше подхода составляет O(n), а пространственная сложность — O(1).
Давайте проверим наш алгоритм на C++, Golang и Javascript.
С++ решение
class Solution { public: vector<int> singleNumber(vector<int>& nums) { int xorResult = nums[0]; int a = 0, b = 0, i; vector<int> result; for(i = 1; i < nums.size(); i++) { xorResult ^= nums[i]; } int setBitNo = xorResult == INT_MIN ? 0 : xorResult & ~(xorResult - 1); for(i = 0; i < nums.size(); i++) { if(nums[i] & setBitNo) { a ^= nums[i]; } else { b ^= nums[i]; } } result.push_back(a); result.push_back(b); return result; } };
Голанг решение
func singleNumber(nums []int) []int { xorResult := 0 for _, num := range nums { xorResult = xorResult ^ num } setBitNo := xorResult & (-xorResult) result := make([]int, 2) for _, num := range nums { if num & setBitNo == 0 { result[0] ^= num } else { result[1] ^= num } } return result }
Javascript-решение
var singleNumber = function(nums) { let xorResult = 0; let a = 0, b = 0, i = 0; for(i = 0; i < nums.length; i++){ xorResult ^= nums[i]; } let setBitNo = xorResult & ~(xorResult - 1); for(i = 0; i < nums.length; i++) { if((nums[i] & setBitNo) === 0) a ^= nums[i]; else b ^= nums[i]; } return [a, b]; };
Давайте протестируем наш алгоритм для примера 1.
Input: nums = [1, 2, 1, 3, 2, 5] Step 1: xorResult = nums[0] = 1 Step 2: int a = 0, b = 0, i vector<int> result Step 3: loop for i = 1; i < nums.size(); i++ xorResult ^= nums[i] The xorResult is 6 (0110) Step 4: setBitNo = xorResult == INT_MIN ? 0 : xorResult & ~(xorResult - 1) = 6 == INT_MIN ? 0 : xorResult & ~(xorResult - 1) = false ? 0 : xorResult & ~(xorResult - 1) = xorResult & ~(xorResult - 1) = 6 & ~(6 - 1) = 6 & ~5 = 6 & -6 = 2 Step 5: loop for i = 0; i < nums.size() 0 < 6 true if nums[i] & setBitNo nums[0] & 2 1 & 2 0001 & 0010 0 false else b ^= nums[i] = b ^ nums[i] = 0 ^ nums[0] = 0 ^ 1 = 1 i++ i = 1 Step 6: loop i < nums.size() 1 < 6 true if nums[i] & setBitNo nums[1] & 2 2 & 2 0010 & 0010 2 true a ^= nums[i] = a ^ nums[1] = 0 ^ 2 = 2 i++ i = 2 Step 7: loop i < nums.size() 2 < 6 true if nums[i] & setBitNo nums[2] & 2 1 & 2 0001 & 0010 0 false else b ^= nums[i] = b ^ nums[i] = 1 ^ nums[2] = 1 ^ 1 = 0 i++ i = 3 Step 8: loop i < nums.size() 3 < 6 true if nums[i] & setBitNo nums[3] & 2 3 & 2 0011 & 0010 2 true a ^= nums[i] = a ^ nums[3] = 2 ^ 3 = 1 i++ i = 4 Step 9: loop i < nums.size() 4 < 6 true if nums[i] & setBitNo nums[4] & 2 2 & 2 0010 & 0010 2 true a ^= nums[i] = a ^ nums[4] = 1 ^ 2 = 3 i++ i = 5 Step 10: loop i < nums.size() 5 < 6 true if nums[i] & setBitNo nums[5] & 2 5 & 2 0101 & 0010 0 false else b ^= nums[i] = b ^ nums[i] = 0 ^ nums[5] = 0 ^ 5 = 5 i++ i = 6 Step 11: loop i < nums.size() 6 < 6 false Step 12: result.push_back(a) result.push_back(3) result = [3] result.push_back(b) result.push_back(5) result = [3, 5] return result We return the result as [3, 5].
Первоначально опубликовано на https://alkeshghorpade.me.