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

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

  • 题41:缺失的第一个正数
  • 1. 示例说明
  • 2. 题目解析
    • - 题意分解
    • - 优化思路
    • - 测量工具
  • 3. 代码展开
    • 1) 标准求解【双层循环】
    • 2) 改进版一【最小正数计数器】
    • 3) 改进版二【数组排序】
    • 4) 改进版三【下标数组缓冲】
    • 5) 改进版四【元素下标和值匹配】
    • 6) 改进版五【极简代码行】
  • 4. 最优算法

本文为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)


- 测量工具

  • 本地化测试说明:LeetCode网站测试运行时数据波动很大,因此需要本地化测试解决这个问题
  • CheckFuncPerf(函数用时和内存占用测试模块)已上传到CSDN,地址在这里:Python算法题集_检测函数用时和内存占用的模块
  • 超时测试用例文件是官网的,已上传到CSDN,地址在这里:力扣算法题超时测试用例:缺失的第一个正数,数组长度10W

3. 代码展开

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

双层循环,标准超时Python算法题集_缺失的第一个正数_第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) 改进版一【最小正数计数器】

最小整数计数器,尝试在遍历的时候跳跃式上升,结果遇到最尴尬的数据 优化失败,更久超时Python算法题集_缺失的第一个正数_第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%Python算法题集_缺失的第一个正数_第3张图片

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%Python算法题集_缺失的第一个正数_第4张图片

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%Python算法题集_缺失的第一个正数_第5张图片

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%Python算法题集_缺失的第一个正数_第6张图片

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 ~

你可能感兴趣的:(Python,算法,python,数据结构,leetcode)