【问题标题】:finding smallest positive integer missing from an unsorted array, hard edition从未排序的数组中找到最小的正整数,硬版
【发布时间】:2018-07-26 10:20:20
【问题描述】:

经典问题

经典的问题陈述是:给定一个未排序的整数数组A,找到A中不存在的最小正整数。

[3, 2, 1, 6, 5] -> 4
[2, 3, 4, 5] -> 1
[1, 2, 3, 4] -> 5

在经典的问题陈述中,你只得到一个数组,你可以找到很多关于这个问题的解决方案和资源,hereherehere

硬版

在我的问题中,给你两个数组AB,长度均为N。构造一个长度为N 的数组C,其“最小缺失整数”是最大可能的。 C[i] 必须是 A[i]B[i]

A = [1, 2, 4, 3], B = [1, 3, 2, 3] =-> C = [1, 2, 4, 3] answer = 5
A = [4, 2, 1, 6, 5], B = [3, 2, 1, 7, 7] =-> C = [3, 2, 1, 6, 5] answer = 4
A = [2, 3], B = [4, 5] =-> C = [2, 5] answer = 1

我们如何在O(N) 最坏情况下的时间和空间复杂度下解决这个问题?

假设:

1<= N <= 100,000
1<= A[i], B[i] <= 100,000,000

【问题讨论】:

  • 为什么是-1,请告诉我?让我解决这个问题..
  • 通常人们不喜欢只包含问题描述的帖子。就我个人而言,我认为这篇特定的帖子没有任何问题。
  • 努力只需要功课,见on-topic
  • 是的,你是对的,我知道这一点。但我已经尝试用经典方式解决这个问题。我应该把我的论文的照片发给你吗?!
  • 信息:这被称为最小排除项。

标签: algorithm


【解决方案1】:

此算法适用于O(n) 数字范围[0..n+2] 内的数字的算术运算,加上处理输入的时间。


  • 将所有不在[1..n] 范围内的数字替换为n+2。这不会改变结果。
  • 用标记为1, 2, 3, ..., n-1, n, n+2 的节点构造一个图,对于所有0 &lt;= i &lt; n,在节点A[i]B[i] 之间存在一条边(假设0 索引数组)。所以该图有n+1 节点和n 边。

现在问题等同于找到一种方法来引导边,使得入度为 0 的最小顶点是最大可能的。


  • 找出图中的连通分量。
  • 对于每个具有v 顶点和e 边的连接组件,e &gt;= v-1

    • 如果e == v-1,它是一棵树。所有指向边的方法都将导致顶点的度数为 0,并且可以证明,对于树中的所有顶点,存在一种引导边的方法,使得它是唯一的度数为 0 的顶点。

      方法:

      以该顶点为树的根,然后使每条边都直接从父节点到子节点。

      当然,最好(贪婪地)选择入度为 0 的顶点作为最大数的顶点。

    • 如果e &gt;= v,则连接的组件内部存在电路。然后,可以对边进行定向,使所有顶点的入度都非零。

      证明(以及构造边缘方向的方法)留给读者。

【讨论】:

  • 似乎我们在同一时间发布了相同(或至少非常相似)的答案 :-)
  • @m69 和 user202729,我赞成你们两个,但不知道我应该接受哪一个。顺便说一句,这两种算法都在 O(N) 中工作,还是 O(A[i])?
  • @EzizDurdyyev 他们都是 O(N);这基本上是相同的想法。我们实际上是在几秒钟内发布的,所以我不知道该告诉你什么。最后,接受是关于“你认为最有帮助”的答案,所以这取决于你。
【解决方案2】:

如果您遍历数组并构建一个图形,其中您在 A 或 B 中遇到的每个唯一整数都成为一个顶点,并且每对整数 A[i] 和 B[i] 在两个顶点之间创建一条边,那么该图最多有 2×N 个顶点,因此该图占用的空间与 N 成线性关系。

然后,对于图中的每条边,我们将决定它的方向,即它连接的两个整数中的哪一个将用于数组 C。最后,我们将再次遍历数组,对于每一对整数,我们查看图中的相应边以了解要使用哪个整数。

我相信决定图中方向所需的操作都可以在 N 的线性时间内完成(如果我错了,请纠正我),所以整个算法应该具有 O(N) 的时间和空间复杂度。

图中决定边方向的规则如下:

  • 图中未连接的部分各自单独处理。
  • 如果(子)图的边数少于顶点数(即它具有没有环的树形结构),则必须牺牲最大整数:将边指向远离该顶点,并将该方向传播到其余的顶点。
  • 如果(子)图的边多于顶点(即它包含至少一个环,可能是由多个边连接的两个顶点),则找到任意一个环,并为其边赋予相同的方向(顺时针或逆时针);然后将方向传播到其他顶点。

在遍历数组并在图中查找相应的边时,标记两个顶点多连接时已使用的整数,以便第二次使用另一个整数。之后,您可以选择其中任何一个。

例子:

A = [1,6,2,9,7,2,5,3,4,10]
B = [5,8,3,2,9,7,1,2,8,3]

图表:

1===5   6---8---4   9---2===3---10
                    |   |
                    --7--

我们找到循环[1>5>1]和[9>2>7>9],以及非循环子图中的最高整数8。

有向图:

1<=>5   6<--8-->4   9-->2<=>3-->10
                    ^   |
                    |-7<-

结果:

C = [1,6,2,2,9,7,5,3,4,10]

第一个缺失的整数是 8,因为我们不得不牺牲它来保存 [6,8,4] 子图中的 4 和 6。

【讨论】:

  • 只有当所有数字都是正数时才正确,例如 A = [-2], B = [1] 失败。
  • @user202729 好吧,标题是“正整数”。
  • @user202729 这些例子表明假设了正输入,但我可能是错的。无论如何,它很容易修复。
  • 输入都是正数,但这没什么大不了的,只要算法对正整数正确工作。
  • 这就是我要说的。你的分数是 user202729 的 10 倍,所以支持一个上升成员会更好。你有什么意见?如果你是我,你会接受哪一个?
【解决方案3】:

请检查这段代码 sn-p,我是用 Java 8 编写的

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;


public class Run {
    private int solution(int[] A) {

        List<Integer> positiveIntegerList = Arrays.stream(A).distinct().filter(e -> e > 0).boxed().sorted(Comparator.naturalOrder()).collect(Collectors.toList());
        final int i = 1;

        if (!positiveIntegerList.isEmpty()) {
            final int missingMax = positiveIntegerList.get(positiveIntegerList.size() - i) + i;
            List<Integer> range = IntStream.rangeClosed(i, positiveIntegerList.get(positiveIntegerList.size() - i))
                    .boxed().collect(Collectors.toList());

            AtomicInteger index = new AtomicInteger();
            return range.stream().filter(e -> !e.equals(positiveIntegerList.get(index.getAndIncrement()))).findFirst().
                    orElse(missingMax);
        } else {
            return i;
        }
    }


    public static void main(String[] args) {
        Run run = new Run();
        int[] intArray = new int[]{1, 3, 6, 4, 1, 2};
        System.out.println(run.solution(intArray));
    }
}

【讨论】:

  • 你应该为你的解决方案添加一些解释。
【解决方案4】:

我已经尝试过在 Python3 中解决这个问题的方法。欢迎提出建议和反馈。谢谢。

  1. 从列表中删除负值
  2. 在列表中插入 0
  3. 使用 sorted and set 功能对列表中的重复项进行排序和删除
  4. 使用枚举器创建索引、值。
  5. 比较从 (0, 0) 开始的索引和值。如果索引和值不匹配,则下一个值是最小的正数。

--

from typing import List

class Solution:
    def firstMissingPositive(self, nums: List[int]) -> int:
        #Remove all the negative values from the list
        positive_list = [i for i in nums if i > 0]

        #Appending Zero to list. In case of no postive values in the list.
        positive_list.append(0)

        #Sort and remove duplicates from the list using Sorted and set function
        sorted_postive_list = sorted(set(positive_list))

        #starting with (index, value) --> (0,0), Compare index and value, and if they don't match then next value should be the smallest positive number
        return next((a for a, b in enumerate(sorted_postive_list, sorted_postive_list[0]) if a != b), sorted_postive_list[-1]+1)

if __name__ == "__main__":
    num = [1,2,3,0]
    a = Solution()
    print(a.firstMissingPositive(num))

【讨论】:

    猜你喜欢
    • 2011-06-28
    • 2014-08-25
    • 2021-11-29
    • 1970-01-01
    • 2021-12-16
    • 2011-07-22
    • 2016-03-20
    • 1970-01-01
    相关资源
    最近更新 更多