• 数组


    1.缺失的第一个正数  

    要求时间复杂度应为O(n),空间复杂度为常数。

    方法一:哈希表(空间复杂度不符合要求)
    按照刚才我们读例子的思路,其实我们只需从最小的正整数 11 开始,依次判断 22、 33 、44 直到数组的长度 N 是否在数组中。

    如果当前考虑的数不在这个数组中,我们就找到了这个缺失的最小正整数。

    由于我们需要依次判断某一个正整数是否在这个数组里,我们可以先把这个数组中所有的元素放进哈希表。接下来再遍历的时候,就可以以 O(1)O(1) 的时间复杂度判断某个正整数是否在这个数组。

    由于题目要求我们只能使用常数级别的空间,而哈希表的大小与数组的长度是线性相关的,因此空间复杂度不符合题目要求。

    参考代码 1:

    Java
    import java.util.HashSet;
    import java.util.Set;
    
    public class Solution {
    
    public int firstMissingPositive(int[] nums) {
    int len = nums.length;
    
    Set<Integer> hashSet = new HashSet<>();
    for (int num : nums) {
    hashSet.add(num);
    }
    
    for (int i = 1; i <= len ; i++) {
    if (!hashSet.contains(i)){
    return i;
    }
    }
    
    return len + 1;
    }
    }

    复杂度分析:

    时间复杂度:O(N),这里 N 表示数组的长度。第 1 次遍历了数组,第 2 次遍历了区间 [1, len] 里的元素。
    空间复杂度:O(N),把 N个数存在哈希表里面,使用了 N 个空间。

    方法二:二分查找(时间复杂度不符合要求)
    根据刚才的分析,这个问题其实就是要我们查找一个元素,而查找一个元素,如果是在有序数组中查找,会快一些;

    因此我们可以将数组先排序,再使用二分查找法从最小的正整数 11 开始查找,找不到就返回这个正整数;

    这个思路需要先对数组排序,而排序使用的时间复杂度是 O(N log N)O(NlogN),是不符合这个问题的时间复杂度要求。

    参考代码 2:

    Java
    import java.util.Arrays;
    
    public class Solution {
    
    public int firstMissingPositive(int[] nums) {
    int len = nums.length;
    Arrays.sort(nums);
    
    for (int i = 1; i <= len; i++) {
    int res = binarySearch(nums, i);
    if (res == -1) {
    return i;
    }
    }
    return len + 1;
    }
    
    private int binarySearch(int[] nums, int target) {
    int left = 0;
    int right = nums.length - 1;
    while (left <= right) {
    int mid = (left + right) >>> 1;
    if (nums[mid] == target) {
    return mid;
    } else if (nums[mid] < target) {
    left = mid + 1;
    } else {
    right = mid - 1;
    }
    }
    return -1;
    }
    }

    复杂度分析:

    时间复杂度:O(N log N),这里 NN 表示数组的长度。时间复杂度主要消耗在排序上,排序使用 O(Nlog N)O(NlogN)。二分查找使用 O(log N)O(logN);
    空间复杂度:O(1)。
    方法三:将数组视为哈希表
    由于题目要求我们“只能使用常数级别的空间”,而要找的数一定在 [1, N + 1] 左闭右闭(这里 N 是数组的长度)这个区间里。因此,我们可以就把原始的数组当做哈希表来使用。事实上,哈希表其实本身也是一个数组;
    我们要找的数就在 [1, N + 1] 里,最后 N + 1 这个元素我们不用找。因为在前面的 N 个元素都找不到的情况下,我们才返回 N + 1;
    那么,我们可以采取这样的思路:就把 11 这个数放到下标为 00 的位置, 22 这个数放到下标为 11 的位置,按照这种思路整理一遍数组。然后我们再遍历一次数组,第 11 个遇到的它的值不等于下标的那个数,就是我们要找的缺失的第一个正数。
    这个思想就相当于我们自己编写哈希函数,这个哈希函数的规则特别简单,那就是数值为 i 的数映射到下标为 i - 1 的位置。

    using namespace std;
    
    class Solution {
    public:
        int firstMissingPositive(vector<int> &nums) {
            for (int i = 0; i < nums.size(); i++) {
                while (nums[i] != i + 1) {
                    if (nums[i] <= 0 || nums[i] > nums.size() || nums[i] == nums[nums[i] - 1])
                        break;
                    // 将nums[i] 放置到对应位置上[1,2,3...]
                    int idx = nums[i] - 1;
                    nums[i] = nums[idx];
                    nums[idx] = idx + 1;
                }
            }
            for (int i = 0; i < nums.size(); i++) {
                if (nums[i] != (i + 1)) {
                    return (i + 1);
                }
            }
            return (nums.size() + 1);
        }
  • 相关阅读:
    设计模式之开篇(C#语法) 爱拼才会赢
    C#语法糖之第一篇:自动属性&隐式类型 爱拼才会赢
    C#语法糖之第四篇: 扩展方法 爱拼才会赢
    C#中this在扩展方法的应用
    笔记20120215_转义字符_运算符
    Linux基本操作
    SQL创表
    软件测试的概念
    java 线程池
    Redis广播
  • 原文地址:https://www.cnblogs.com/lau1997/p/12805712.html
Copyright © 2020-2023  润新知