力扣之数组重复/缺失/超过一半的元素题解大全【持续更新】

最近刷力扣总能碰到各种关于数组数字元素的题目,而且这些题基本都有很多巧妙的多重解法,博主自己想不出,只能靠大量的记录了。

287. 寻找重复数

在这里插入图片描述
不考虑题目的限制条件,立马想到的解法是:
①边遍历边加入set, 直到出现一个原本就存在set中的元素
②遍历并交换数组元素到相应位置上[数字x应填入下标x-1中], 直到出现一个元素与相应位置相等, 就找到了重复元素了
但是以上解法1需要用到额外的o(n)空间, 解法2需要修改原数组, 都不满足条件…这时猪脑过载了, 只能去看大佬解析

解法一: 二分查找

仔细推敲后可以发现如果对1至n的元素x, 去统计nums中小于等于x的元素个数, 记为cnt(x)。 假设重复元素是target, 那么对于target前的那些元素, cnt(x)一定是小于等于x的, 当x恰好为target时, cnt(target) > target, 并且对于x>target的那些元素, 都有cnt(x) > x, 那么二分的性质就出来了,target是界: 问题转化为找到第一个cnt(x) > x 的元素, 该元素就为重复元素。更为细致的证明手推如下:
在这里插入图片描述

解法二: 另一种更普适的二分

另一种二分思路是参考剑指offer书P41,若区间[l, r]有重复元素,统计[l, mid]的元素个数,若个数大于区间数则说明该段必包含重复元素,否则另一段含重复元素。
这种方法还适用于多重复元素的情形,但最终只能找到其中一个重复元素。
注意:多重复元素不能用第一种二分,因为cnt(i)-i的单调性质不能保证

class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        def countNums(numbers, m, n):
            return sum(True for number in numbers if m<=number<=n)

        st, ed = 1, len(nums)-1
        while st < ed:
            mid = (st+ed)//2
            if countNums(nums, st, mid) > mid-st+1:
                ed = mid 
            else:
                st = mid + 1
        return st

解法三: 数组成环+找环的入口

建立下标和值的映射关系 i -> nums[i], 形成链表。以数组 [1,3,4,2]为例, 形成了 0->1->3->2->4, 如果数组中有重复的数,以数组 [1,3,4,2,2]为例, 形成了0->1->3->2->4->2->4->2->……如下
在这里插入图片描述

反证法可以知道有环就一定有重复, 但有重复就一定有环吗? 是的。因为这种做法会倾向于遍历全部元素, 直到找到一个重复元素形成环, 注意: 形成环后不一定能遍历完所有元素了。

1.数组中有一个重复的整数 <> 链表中存在环
2.找到数组中的重复整数 <
> 找到链表的环入口

那么问题就变成了环形链表找入口问题了:

class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        fast, slow = 0, 0
        while True:
            fast = nums[nums[fast]]
            slow = nums[slow]
            if fast == slow: 
                break
        ## 相遇
        slow = 0
        while fast != slow:
            fast = nums[fast]
            slow = nums[slow]
        return fast

JZ3 数组中重复的数字

在这里插入图片描述

交换法[原地哈希]

class Solution:
    def duplicate(self , numbers: List[int]) -> int:
        # write code here
        for i in range(len(numbers)):
            while numbers[i] != i:
                if numbers[i] == numbers[numbers[i]]:
                    return numbers[i]
                numbers[numbers[i]], numbers[i] = numbers[i], numbers[numbers[i]]
        return -1

268. 丢失的数字

在这里插入图片描述
在这里插入图片描述

交换法[原地哈希]

数学法

异或

数组nums 中有 n 个数,在这 n 个数的后面添加从 0 到 n 的每个整数,则添加了 n+1
个整数,共有 2n+1 个整数。
在 2n+1 个整数中,丢失的数字只在后面 n+1个整数中出现一次,其余的数字在前面 n 个整数中(即数组中)和后面n+1 个整数中各出现一次,即其余的数字都出现了两次。

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        xor = 0
        for i, num in enumerate(nums):
            xor ^= i ^ num
        return xor ^ len(nums)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值