0
点赞
收藏
分享

微信扫一扫

剑指offer(第二版)python刷题——找出数组中任一重复元素

fbd4ffd0717b 2022-02-06 阅读 55

JZ3:一维数组中找出任一重复的元素


```python
'''
JZ3:输入一个长为n的数组,元素取值为0-n-1,找出数组中的人一个重复元素
解题思路:
1、边界条件:空数组/无重复情况返回-1
2、解法:
    A1 排序后遍历比较相邻两个元素是否相等
    A2 用空间换时间,用列表、集合、字典等容器储存唯一值
    A3 重排序索引对齐,将索引和值大小对应,不能对应的即重复元素:依次遍历,不对应的元素放到相应位置,交换值
    A4 二分查找,不断缩小取值区间,直到left=right,并且区间长度<元素个数
3、测试
    T1 空数组
    T2 长度为1的数组
    T3 长度为2的数组
'''
from typing import List

class Solution:

    def duplicate(self, numbers: List [int])->int:
        '''排序后遍历
           时间复杂度:排序 nlogn + n次循环
           空间复杂度:O(1)'''
        numbers.sort()
        if len(numbers) == 1:
            return -1
        for i in range(len(numbers)):
            if numbers[i] == numbers[i+1]:
                return numbers[i]
        return -1

    def duplicate1(self, numbers: List[int])->int:
        '''使用容器存储唯一值
           LIST/SET/DCIT
           时间复杂度O(n) 空间复杂度O(n)
           以空间换时间'''
        value_set = set()
        for num in numbers:
            if num in value_set:
                return num
            else:
                value_set.add(num)
        return -1

    def duplicate2(self, numbers: List[int])->int:
        '''
        索引对齐数组值,将空间复杂度降为O(1)
        1、从头遍历数组,如果索引对齐进入下次迭代
        2、如果索引未对齐,将交换元素,继续比较索引是否对齐:每个元素最多交换两次即可对齐
        3、停止条件:交换的两元素相等、未找到重复元素返回-1
        '''
        for i in range(len(numbers)):
            while numbers[i] != i:
                if numbers[i] == numbers[numbers[i]]:
                    return numbers[i]
                else:
                    temp = numbers[i]
                    numbers[i] = numbers[temp]
                    # 继续比较新的numbers[i]是否对齐
                    numbers[temp] = temp
        return -1

    def count_range(self, numbers: List[int], left, right)->int:
        if len(numbers) == 0:
            return 0
        count = 0
        for i in range(len(numbers)):
            if numbers[i]>=left and numbers[i]<=right:
                count += 1
        return count


    def duplicate3(self, numbers: List[int])->int:
        '''
        1、 取值范围0~n-1:分割此区间
        2、 当元素数量大于区间长度,必然存在重复的元素
        3、 继续分割区间直到区间长度为1,但是count>1时,返回left/right
        '''
        if len(numbers) == 0:
            return -1
        left, right = 0, len(numbers)-1
        while left <= right:
            mid = (left + right) // 2
            count = self.count_range(numbers, left, mid)
            if left == right:
                if count > 1:
                    return left
                else:
                    break
            if count > mid - left + 1:
                right = mid
            else:
                left = mid + 1
        return -1



if __name__ == "__main__":
    solution = Solution()
    # 排序后遍历
    nums = [2, 3, 1, 0, 2, 5, 3]
    print(solution.duplicate(nums))
    nums1 = []
    print(solution.duplicate(nums1))
    nums2 = [0]
    print(solution.duplicate(nums2))
    nums3 = [1, 1]
    print(solution.duplicate(nums3))
    # 使用额外空间存储唯一值:空间换时间
    print("_______"*8)
    print(solution.duplicate1(nums))
    print(solution.duplicate1(nums1))
    print(solution.duplicate1(nums2))
    print(solution.duplicate1(nums3))
    # 索引对齐
    print("_______" * 8)
    print(solution.duplicate2(nums))
    print(solution.duplicate2(nums1))
    print(solution.duplicate2(nums2))
    print(solution.duplicate2(nums3))
    # 二分查找
    print("_______" * 8)
    print(solution.duplicate3(nums))
    print(solution.duplicate3(nums1))
    print(solution.duplicate3(nums2))
    print(solution.duplicate3(nums3))


举报

相关推荐

0 条评论