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. 题目解析
- 题意分解
- 本题为求数组元素中缺少的最小正整数
- 本题的主要计算有2处,1是元素遍历,2是比较计算
- 基本的解法是双层循环,第1层正整数从小到大,第二层遍历数组查整数是否存在,所以基本的时间算法复杂度为O(n^2)
- 优化思路
-
通常优化:减少循环层次
-
通常优化:增加分支,减少计算集
-
通常优化:采用内置算法来提升计算速度
-
分析题目特点,分析最优解
-
数组未排序,如果排序将会减少遍历元素个数,排序的时间复杂度是O(n log n)
-
生成一个元素值为0的长度为传入数组长度的数组,通过一次遍历,将元素下标的计数加1,算法的空间复杂度为O(n)
-
第一次遍历将元素A与下标为A的元素比较,不存在的元素标记为-1,第二遍遍历找值为-1,算法时间复杂度O(n),空间复杂度O(1)
-
- 测量工具
- 本地化测试说明:LeetCode网站测试运行时数据波动很大,因此需要本地化测试解决这个问题
CheckFuncPerf
(函数用时和内存占用测试模块)已上传到CSDN,地址在这里:Python算法题集_检测函数用时和内存占用的模块- 超时测试用例文件是官网的,已上传到CSDN,地址在这里:力扣算法题超时测试用例:缺失的第一个正数,数组长度10W
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 ~