The problem. You're given a sorted array in which every value appears exactly twice, except one value that appears once. Return that single value. Your solution must run in O(log n) time and O(1) space.
nums = [1, 1, 2, 3, 3, 4, 4, 8, 8] -> 2
nums = [3, 3, 7, 7, 10, 11, 11] -> 10
nums = [5] -> 5The array has an odd length, and pairs fill it two by two. Until the single value appears, every pair starts at an even index: nums[0] == nums[1], nums[2] == nums[3], …. The single value pushes everything after it one place right, so from there on pairs start at odd indexes. Take an even mid: if nums[mid] == nums[mid + 1], the pairs are still in step, so the single value is beyond this pair — low = mid + 2. If not, the shift already happened: the single value is at mid or before — high = mid.
a ^ a = 0 and 0 ^ b = b, so XOR-ing every value cancels the pairs and leaves the single one. It works on unsorted input too.
function singleNonDuplicate(nums) { let x = 0; for (const v of nums) x ^= v; // pairs cancel out return x;}Time O(n), space O(1). Neat, but it ignores the sorting and misses the O(log n) requirement.
function singleNonDuplicate(nums) { for (let i = 0; i + 1 < nums.length; i += 2) { if (nums[i] !== nums[i + 1]) return nums[i]; // the first broken pair starts with the single one } return nums[nums.length - 1]; // no broken pair: it's the last value}Time O(n), space O(1). This is the idea binary search speeds up: find the first broken pair.
Look only at even indexes: an intact pair there means the single value is further right.
function singleNonDuplicate(nums) { let low = 0; let high = nums.length - 1; while (low < high) { let mid = low + Math.floor((high - low) / 2); if (mid % 2 === 1) mid--; // look at the start of a pair: an even index if (nums[mid] === nums[mid + 1]) low = mid + 2; // pair intact: the single one is further right else high = mid; // pair broken: the single one is at mid or before } return nums[low];}Time O(log n), space O(1). low and high stay even (both start even and move by even amounts), and the answer is always at an even index, so the range always holds it. mid + 1 is always a valid index because mid < high.
[5].[1, 2, 2].[1, 1, 2].