leetcode
leetcode 501 ~ 550
有序数组中的单一元素

有序数组中的单一元素

难度:

标签:

题目描述

给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。

请你找出并返回只出现一次的那个数。

你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。

 

示例 1:

输入: nums = [1,1,2,3,3,4,4,8,8]
输出: 2

示例 2:

输入: nums =  [3,3,7,7,10,11,11]
输出: 10

 

提示:

  • 1 <= nums.length <= 105
  • 0 <= nums[i] <= 105

代码结果

运行时间: 20 ms, 内存: 22.9 MB


/*
 * 思路:
 * 虽然题目要求O(log n)时间复杂度,通常使用二分查找。 
 * 但可以使用Java Stream结合数组进行二进制搜索。 
 * 这种方法更简洁但可能不满足题目对时间复杂度的严格要求。
 */
import java.util.Arrays;
import java.util.stream.IntStream;
 
public class Solution {
    public int singleNonDuplicate(int[] nums) {
        return IntStream.range(0, nums.length)
                        .filter(i -> i % 2 == 0 && (i == nums.length - 1 || nums[i] != nums[i + 1]))
                        .map(i -> nums[i])
                        .findFirst()
                        .orElse(-1); // 理论上不可能到达这个地方
    }
}

解释

方法:

这个题解使用了二分查找的思路。因为数组是有序的,且除了一个元素只出现一次外其他元素都出现两次,所以可以利用元素的下标的奇偶性来缩小查找范围。如果 mid 是偶数下标,则比较 nums[mid] 和 nums[mid+1],如果两者相等,说明单一元素在 mid 之后,否则在 mid 及之前。如果 mid 是奇数下标,则比较 nums[mid] 和 nums[mid-1],如果两者相等,说明单一元素在 mid 之后,否则在 mid 及之前。最终 begin 和 end 会指向同一位置,即为单一元素的下标。

时间复杂度:

O(logn)

空间复杂度:

O(1)

代码细节讲解

🦆
在二分查找的过程中,为何当`mid`是偶数时比较`nums[mid]`和`nums[mid+1]`,而当`mid`是奇数时比较`nums[mid]`和`nums[mid-1]`?
在有序数组中,除了一个元素出现一次外,其余元素都成对出现。当`mid`是偶数时,如果`nums[mid]`和`nums[mid+1]`相等,说明`mid`和`mid+1`是一对,因此单一元素在`mid+2`及其之后的部分。反之,如果不相等,则说明单一元素在`mid`或`mid`之前的部分。当`mid`是奇数时,如果`nums[mid]`和`nums[mid-1]`相等,这也是一对,所以单一元素在`mid+1`及之后的部分。这种比较方式保证了每次都能排除至少一个已配对的元素,从而缩小查找范围。
🦆
题解中提到如果`nums[mid] == nums[mid+1]`则单一元素在`mid`之后,能否具体解释为什么这样判断会正确?
如果`nums[mid] == nums[mid+1]`且`mid`为偶数,这意味着从数组开始到`mid`的位置,元素应该完全成对。因为成对元素的起始索引应为偶数,结尾索引为奇数,这对在`mid`和`mid+1`位置形成完整对。由此可以断定,单个出现的元素必定在`mid+2`或之后的位置。逻辑上,这确保了所有`mid`之前的元素都不可能是单一元素。
🦆
如果数组长度为奇数,二分查找的循环条件为`while begin < end`,在这种情况下循环是否会遗漏检查某些元素?
不会遗漏检查任何元素。循环条件`while begin < end`确保了只要`begin`不等于`end`,查找过程就会继续。在每次迭代中,要么`begin`增加,要么`end`减少,逐渐缩小查找范围。由于单一元素的存在,最终`begin`和`end`会聚焦在单一元素上。由于数组长度为奇数且除一元素外所有元素成对出现,最终这对元素将必然指向单一元素。
🦆
在二分查找过程结束时,为什么可以直接返回`nums[end]`作为结果,而不需要进一步验证?
当二分查找的过程结束时,`begin`和`end`会指向同一个位置,这个位置就是未成对的单一元素。这是因为二分查找的逻辑确保每次都排除了成对的元素,并且不断缩小范围直到只剩下一个元素。由于题目假设保证有一个且只有一个单一元素,因此这个位置上的元素就是答案,无需进一步验证。

相关问题