Python算法题集_缺失的第一个正数

本文为Python算法题集之一的代码示例

题41:缺失的第一个正数

1. 示例说明

 给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

示例 1:

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

示例 2:

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

示例 3:

输入:nums = [7,8,9,11,12]
输出:1

提示:

  • 1 <= nums.length <= 5 * 105

  • -231 <= nums[i] <= 231 - 1


2. 题目解析

- 题意分解

  1. 本题为求数组元素中缺少的最小正整数
  2. 本题的主要计算有2处,1是元素遍历,2是比较计算
  3. 基本的解法是双层循环,第1层正整数从小到大,第二层遍历数组查整数是否存在,所以基本的时间算法复杂度为O(n^2)

- 优化思路

  1. 通常优化:减少循环层次

  2. 通常优化:增加分支,减少计算集

  3. 通常优化:采用内置算法来提升计算速度

  4. 分析题目特点,分析最优解

    1. 数组未排序,如果排序将会减少遍历元素个数,排序的时间复杂度是O(n log n)

    2. 生成一个元素值为0的长度为传入数组长度的数组,通过一次遍历,将元素下标的计数加1,算法的空间复杂度为O(n)

    3. 第一次遍历将元素A与下标为A的元素比较,不存在的元素标记为-1,第二遍遍历找值为-1,算法时间复杂度O(n),空间复杂度O(1)


- 测量工具


3. 代码展开

1) 标准求解【双层循环】

双层循环,标准超时在这里插入图片描述

import CheckFuncPerf as cfp

def firstMissingPositive_base(nums):
 ilen = len(nums)
 for iIdx in range(1, ilen+1):
     for jIdx in range(ilen):
         bfind = nums[jIdx] == iIdx
         if bfind:
            break
     if not bfind:
         return iIdx
 return ilen+1

testcase_big = open(r'testcase/hot17_big.txt', mode='r', encoding='utf-8').read().replace('[', '').replace(']', '')
testcase_big = testcase_big.split(',')
nums = [int(x) for x in testcase_big]
result = cfp.getTimeMemoryStr(firstMissingPositive_base, nums)
print(result['msg'], '执行结果 = {}'.format(len(result['result'])))

# 运行结果
函数 firstMissingPositive_base 的运行时间为 280613.32 ms;内存使用量为 20.00 KB 执行结果 = 100001

2) 改进版一【最小正数计数器】

最小整数计数器,尝试在遍历的时候跳跃式上升,结果遇到最尴尬的数据 优化失败,更久超时在这里插入图片描述

import CheckFuncPerf as cfp

def firstMissingPositive_ext1(nums):
 ilen = len(nums)
 imin = 1
 while imin < ilen+1:
     bfind = False
     for jIdx in range(ilen):
         bfind = bfind or nums[jIdx] == imin
         if nums[jIdx] == imin:
             imin += 1
     if not bfind:
         return imin
 return imin

testcase_big = open(r'testcase/hot17_big.txt', mode='r', encoding='utf-8').read().replace('[', '').replace(']', '')
testcase_big = testcase_big.split(',')
nums = [int(x) for x in testcase_big]
result = cfp.getTimeMemoryStr(firstMissingPositive_ext1, nums)
print(result['msg'], '执行结果 = {}'.format(len(result['result'])))

# 运行结果
函数 firstMissingPositive_ext1 的运行时间为 670308.17 ms;内存使用量为 0.00 KB 执行结果 = 100001

3) 改进版二【数组排序】

传入数组使用标准排序函数,然后单层循环 表现优异,超过94%在这里插入图片描述

import CheckFuncPerf as cfp

def firstMissingPositive_ext2(nums):
 ilen = len(nums)
 nums.sort()
 imin = 1
 for iIdx in range(ilen):
     if nums[iIdx] < imin:
         continue
     if imin == nums[iIdx]:
         imin += 1
     else:
         return imin
 return imin

testcase_big = open(r'testcase/hot17_big.txt', mode='r', encoding='utf-8').read().replace('[', '').replace(']', '')
testcase_big = testcase_big.split(',')
nums = [int(x) for x in testcase_big]
result = cfp.getTimeMemoryStr(firstMissingPositive_ext2, nums)
print(result['msg'], '执行结果 = {}'.format(len(result['result'])))

# 运行结果
函数 firstMissingPositive_ext2 的运行时间为 10.00 ms;内存使用量为 0.00 KB 执行结果 = 100001

4) 改进版三【下标数组缓冲】

生成下标数组,按下标累计数字出现次数,然后单层循环 极速狂飙,超越96%在这里插入图片描述

import CheckFuncPerf as cfp

def firstMissingPositive_ext3(nums):
 ilen = len(nums)
 numscopy = [0] * ilen
 for iIdx in range(ilen):
     if nums[iIdx] < 1:
         continue
     if nums[iIdx] > ilen:
         continue
     numscopy[nums[iIdx]-1] += 1
 for iIdx in range(ilen):
     if numscopy[iIdx] < 1:
         return iIdx+1
 return ilen+1

testcase_big = open(r'testcase/hot17_big.txt', mode='r', encoding='utf-8').read().replace('[', '').replace(']', '')
testcase_big = testcase_big.split(',')
nums = [int(x) for x in testcase_big]
result = cfp.getTimeMemoryStr(firstMissingPositive_ext3, nums)
print(result['msg'], '执行结果 = {}'.format(len(result['result'])))

# 运行结果
函数 firstMissingPositive_ext3 的运行时间为 16.98 ms;内存使用量为 720.00 KB 执行结果 = 100001

5) 改进版四【元素下标和值匹配】

尝试对元素值是否等于下标【正数-1】进行匹配 指标良好,超过81%在这里插入图片描述

import CheckFuncPerf as cfp

def firstMissingPositive_ext4(nums):
 ilen = len(nums)
 for iIdx in range(ilen):
     bNeedFix = True
     while bNeedFix:
         bNeedFix = False
         if nums[iIdx] == iIdx+1:
             continue
         if nums[iIdx] < 1:
             continue
         elif nums[iIdx] > ilen:
             continue
         if nums[iIdx] != nums[nums[iIdx] - 1]:
             nums[nums[iIdx] - 1], nums[iIdx] = nums[iIdx], nums[nums[iIdx] - 1]
             bNeedFix = True
 for iIdx in range(ilen):
     if nums[iIdx] != iIdx+1:
         return iIdx+1
 return ilen+1

testcase_big = open(r'testcase/hot17_big.txt', mode='r', encoding='utf-8').read().replace('[', '').replace(']', '')
testcase_big = testcase_big.split(',')
nums = [int(x) for x in testcase_big]
result = cfp.getTimeMemoryStr(firstMissingPositive_ext4, nums)
print(result['msg'], '执行结果 = {}'.format(len(result['result'])))

# 运行结果
函数 firstMissingPositive_ext4 的运行时间为 30.02 ms;内存使用量为 4.00 KB 执行结果 = 100001

6) 改进版五【极简代码行】

采用下标、值匹配算法,极简代码行指标良好,超过87%在这里插入图片描述

import CheckFuncPerf as cfp

def firstMissingPositive_short(nums):
 ilen = len(nums)
 for iIdx in range(ilen):
     while 1 <= nums[iIdx] <= ilen and nums[iIdx] != nums[nums[iIdx]-1]:
         nums[nums[iIdx]-1], nums[iIdx] = nums[iIdx], nums[nums[iIdx]-1]
 for iIdx in range(ilen):
     if nums[iIdx] != iIdx+1:
         return iIdx+1
 return ilen+1

testcase_big = open(r'testcase/hot17_big.txt', mode='r', encoding='utf-8').read().replace('[', '').replace(']', '')
testcase_big = testcase_big.split(',')
nums = [int(x) for x in testcase_big]
result = cfp.getTimeMemoryStr(firstMissingPositive_short, nums)
print(result['msg'], '执行结果 = {}'.format(len(result['result'])))

# 运行结果
函数 firstMissingPositive_short 的运行时间为 18.98 ms;内存使用量为 0.00 KB 执行结果 = 100001

4. 最优算法

根据本地日志分析,最优算法为第3种firstMissingPositive_ext2

testcase_big = open(r'testcase/hot17_big.txt', mode='r', encoding='utf-8').read().replace('[', '').replace(']', '')
testcase_big = testcase_big.split(',')
nums = [int(x) for x in testcase_big]

# 6种算法本地速度实测比较
函数 firstMissingPositive_base 的运行时间为 280613.32 ms;内存使用量为 20.00 KB 执行结果 = 100001
函数 firstMissingPositive_ext1 的运行时间为 670308.17 ms;内存使用量为 0.00 KB 执行结果 = 100001
函数 firstMissingPositive_ext2 的运行时间为 10.00 ms;内存使用量为 0.00 KB 执行结果 = 100001
函数 firstMissingPositive_ext3 的运行时间为 16.98 ms;内存使用量为 720.00 KB 执行结果 = 100001
函数 firstMissingPositive_ext4 的运行时间为 30.02 ms;内存使用量为 4.00 KB 执行结果 = 100001
函数 firstMissingPositive_short 的运行时间为 18.98 ms;内存使用量为 0.00 KB 执行结果 = 100001

一日练,一日功,一日不练十日空

may the odds be ever in your favor ~

  • 15
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长孤秋落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值