数组-剑指offer3数组中重复的数字-简单-20210806

数组-剑指offer3数组中重复的数字-简单-20210806

1. 题目描述

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任一一个重复的数字。

示例

例如,如果输入长度为7的数组[2,3,1,0,2,5,3],那么对应的输出是2或者3。存在不合法的输入的话输出-1

2. 题目解答

2.1 第一次尝试答案-原地交换

时间复杂度:O(n)

空间复杂度:O(1)

# @param numbers int整型一维数组 
# @return int整型
class Solution:
    def duplicate(self, numbers):
        # write code here

        # illegal input
        if not numbers or len(numbers) <= 0:
            return -1
        for i in range(len(numbers)):
            if numbers[i] < 0 or numbers[i] >= len(numbers): # 注意是大于等于
                return -1

        # find duplicate number
        for i in range(len(numbers)):
            while numbers[i] != i:
                # 相等,在下表i和m均有出现
                if numbers[i] == numbers[numbers[i]]:
                    return numbers[i]
                # 不等,交换索引和值对应的索引位置
                else:
                    temp = numbers[i]
                    numbers[i] = numbers[temp]
                    numbers[temp] = temp
            return -1  # 需要考虑没有重复数字时候的情况

2.2 第二种解法(较优)

  • ① 初始化: 新建 HashSet ,记为 dic ;
  • ② 遍历数组 nums中的每个数字 num :
    • 当num不在dic中,将 num 添加至 dic 中;
    • 当 num 在 dic 中,说明重复,直接返回 num
  • ③ 最后返回 -1,说明无重复数字

时间复杂度:O(n)

空间复杂度:O(n)

class Solution:
    def findRepeatNumber(self, nums):
        dic = set()
        for num in nums:
            if num in dic: return num
            dic.add(num)
        return -1

2.3 第三种解法(效率差)

  • ① 先把输入的数组排序 O(nlogn) 快排复杂度
  • ② 从头扫描找到重复数组

时间复杂度:O(nlogn)

空间复杂度:O(n)

class Solution:
    def findRepeatNumber(self, nums):
        # 排序
        nums.sort()
        
        # 找重复数字
        temp = 0
        for i in range(1, len(nums)):
            if nums[i] == nums[i-1]:
                return nums[i]
        return -1
            
       

3. 测试用例

1. 空数组
2. 数组内数字不在1-n范围内,小于1或大于n两种可能
3. 特殊数组

4. 包含1个或多个重复数字
5. 不包含重复数字

数组-剑指offer3数组中重复的数字(不修改数组)

1. 题目描述:

不修改数组找出重复数字

在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。

示例

如果输入长度为8的数组{2, 3, 5, 4, 3, 2, 6, 7},那么对应的输出是重复的数字2或3

2. 题目解答

2.1 同上解法2 创建一个新数组

空间占用高

时间复杂度:O(n)

空间复杂度:O(n)

class Solution(object):
    def findRepeatNumber(self, nums):
        dic = set()
        for num in nums:
            if num in dic: return num
            dic.add(num)
        return -1

2.2 二分法

以时间换空间

时间复杂度:O(nlogn)

空间复杂度:O(1)

思路:

会有一个缺点:不能找出所有重复的数字,如1~2范围内2出现2次,而1不出现,该算法不能判别

    def countRange(self, numbers, length, start, end):
        count = 0
        for i in range(length):
            if (numbers[i] >= start) and (numbers[i] <= end):
                count += 1
        return count

    def dichotomy(self, numbers):
        '''
        array: n+1
        range: 1~n
        '''
        if not numbers or len(numbers) <= 0:
            return -1
        for num in numbers:
            if num < 1 or num >= len(numbers): return -1

        # find duplicate numbers
        start, end = 1, len(numbers)-1
        while end >= start:
            middle = (start + end) // 2
            count = self.countRange(numbers, len(numbers), start, middle)
            if end == start:
                if count > 1:
                    return start
                else:
                    break
            if count > (middle - start + 1):
                end = middle
            else:
                start = middle + 1
        return -1
# !/usr/bin/env python
# --*--coding:utf-8-*-
__author__ = 'Xue Wang'

# @param numbers int整型一维数组
# @return int整型
class Solution(object):
    def countRange(self, nums, l, r):
        count = 0
        for i in range(len(nums)):
            if l <= nums[i] <= r:  # bug:<= 写错了
                count += 1
        return count

    def findDuplicate(self, nums):
        if not nums or len(nums) <= 0:
            return -1
        for num in nums:
            if num < 1 or num >= len(nums):
                return -1
        # find duplicate numbers
        l, r = 1, len(nums)-1
        while r >= l:
            mid = (r + l) // 2
            count = self.countRange(nums, l, mid)
            if l == r:
                if count > 1: return l
                else: break
            if count > mid - l + 1:
                r = mid
            else:
                l = mid + 1
        return -1


if __name__ == '__main__':
    # Test Case  0~n-1
    # 注意测试用例范围,否则会溢出
    nums_1 = []  # 空数组
    nums_2 = [1]  # 只有一个值
    nums_3 = [-1, 0]  # 元素不符合
    nums_4 = [1, 2, 0]  # 不包含重复数字
    nums_5 = [1, 2, 3, 4, 3]
    nums_6 = [2, 2, 5, 4, 3]  # no pass
    nums_7 = [2, 2, 5, 9, 3]  # 超出规定范围

    Solution = Solution()
    print(Solution.findDuplicate(nums_1))
    print(Solution.findDuplicate(nums_2))
    print(Solution.findDuplicate(nums_3))
    print(Solution.findDuplicate(nums_4))
    print(Solution.findDuplicate(nums_5))
    print(Solution.findDuplicate(nums_6))
    print(Solution.findDuplicate(nums_7))


2.3 二分法-递归(错解)

class Solution:
    def findDuplicate(self,num,l,r):
        if num == None or len(num) <= 0:     # 排除空数组
                return False
        for i in range(len(num)):
            if num[i] < 0 or num[i] >= len(num):    # 若数组元素不在1到n,也排除
                return False
        if l == r:     # 如果区间左右指针相等,说明已经找到重复元素,直接返回
            return l
        mid = (l + r) // 2   # 中间元素
        count = 0            # 计数变量
        for i in range(len(num)):     # 遍历
            if l <= num[i] <= mid:   # 如果数组元素出现在 1 到 mid 之间,计数变量加1
                count += 1
        if count > mid - l + 1:      # 若计数变量大于1 到 mid之间元素的个数,说明重复数字在1到 mi区间内。
            return loop_search(num, l, mid)    # 则更新区间,继续寻找重复元素
        else:
            return loop_search(num, mid+1, r)   # 反之亦然

if __name__ == '__main__':

# 验证

    # 长度为n的数组里包含一个或多个重复的数字。
    test_1 = [2,3,5,4,3,2,6,7]
    
    # 数组中不包含重复的数字
    test_2 = [1,5,3,2,4]
    
    # 空数组
    test_3 = None
    
    # 数组元素超出要求
    test_4 = [4,3,2,9]
    
    solution = Solution()
    print("test_1:", solution.re_search(test_1,1,len(num)))
    print("test_2:", solution.re_search(test_2,1,len(num)))
    print("test_3:", solution.re_search(test_3,1,len(num)))
    print("test_4:", solution.re_search(test_4,1,len(num)))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值