Algorithm O(n)相交中的排序 设S1和S2为两组整数(它们不一定是不相交的) 我们知道 即|S1 |=| S2 |=n(即每组都有n整数) 每组存储在长度为n的数组中,其中 它的整数按升序排序 让k≥ 1必须是整数 设计一个算法来查找 kS1中的最小整数∩ S2在O(n)时间内

Algorithm O(n)相交中的排序 设S1和S2为两组整数(它们不一定是不相交的) 我们知道 即|S1 |=| S2 |=n(即每组都有n整数) 每组存储在长度为n的数组中,其中 它的整数按升序排序 让k≥ 1必须是整数 设计一个算法来查找 kS1中的最小整数∩ S2在O(n)时间内,algorithm,sorting,time-complexity,hashset,Algorithm,Sorting,Time Complexity,Hashset,这就是我到目前为止所做的: 创建一个名为Intersection 对于S1中的每个e在O(n)时间内将e添加到hashset 对于S2中的每个e,检查hashset中的e在O(n)时间内是否存在 如果哈希集中存在e,则将e添加到交叉点 比较完成后,按计数排序,在O(n)时间内排序 返回第一个k整数 因此O(n)+O(n)+O(n)=O(n) 我走对了吗?是的,你肯定走对了,但实际上根本不需要生成哈希表或额外的集。由于您的两个集合已经排序,您只需在它们之间运行一个索引/指针,查找公共元素 例如,要

这就是我到目前为止所做的:

  • 创建一个名为
    Intersection
  • 对于
    S1
    中的每个
    e
    在O(n)时间内将
    e
    添加到hashset
  • 对于
    S2
    中的每个
    e
    ,检查hashset中的
    e
    在O(n)时间内是否存在
  • 如果哈希集中存在
    e
    ,则将
    e
    添加到
    交叉点
  • 比较完成后,按计数排序,在O(n)时间内排序
  • 返回第一个
    k
    整数
  • 因此O(n)+O(n)+O(n)=O(n)


    我走对了吗?

    是的,你肯定走对了,但实际上根本不需要生成哈希表或额外的集。由于您的两个集合已经排序,您只需在它们之间运行一个索引/指针,查找公共元素

    例如,要从两个集合中查找第一个公共元素,请使用以下伪代码:

    start at first index of both sets
    while more elements in both sets, and current values are different:
        if set1 value is less than set2 value:
            advance set1 index
        else
            advance set2 index
    
    最后,
    set1 index
    将引用一个相交点,前提是两个索引都没有超出各自列表中的最后一个元素。然后,您可以在循环中使用该方法来查找第一个
    x
    交点值

    这里是Python3中的概念证明,它给出了两个列表中的前三个数字(2的倍数和3的倍数)。完整的交叉点将是
    {0,6,12,18,24}
    ,但您将看到它将只提取其中的前三个:

    # Create the two lists to be used for intersection.
    
    set1 = [i * 2 for i in range(15)] ; print(set1) # doubles
    set2 = [i * 3 for i in range(15)] ; print(set2) # trebles
    
    idx1 = 0 ; count1 = len(set1)
    idx2 = 0 ; count2 = len(set2)
    
    # Only want first three.
    
    need = 3
    while need > 0:
        # Continue until we find next intersect or end of a list.
    
        while idx1 < count1 and idx2 < count2 and set1[idx1] != set2[idx2]:
            # Advance pointer of list with lowest value.
    
            if set1[idx1] < set2[idx2]:
                idx1 += 1
            else:
                idx2 += 1
    
        # Break if reached end of a list with no intersect.
    
        if idx1 >= count1 or idx2 >= count2:
            break
    
        # Otherwise print intersect and advance to next list candidate.
    
        print(set1[idx1]) ; need -= 1
        idx1 += 1 ; idx2 += 1
    
    如果在末尾需要一个列表,而不仅仅是打印出相交点,那么只需在循环之前初始化一个空容器,然后将值附加到它,而不是打印它。然后,这会变得更像您提出的解决方案,但其优点是不需要哈希表或排序。

    在Python中:

    i1= 0; i2= 0
    while k > 0 and i1 < n and i2 < n:
        if S1[i1] < S2[i2]:
            i1+= 1
        elif S1[i1] > S2[i2]:
            i2+= 1
        else:
            Process(S1[i1], S2[i2])
            i1+= 1; i2+= 1
            k-= 1
    
    i1=0;i2=0
    当k>0和i1S2[i2]:
    i2+=1
    其他:
    过程(S1[i1],S2[i2])
    i1+=1;i2+=1
    k-=1
    

    如果交集中没有足够多的元素,执行将执行少于
    k
    Process
    调用。

    创建两个数组,分别调用
    arr1
    arr2
    ,大小为
    array\u size
    ,并以升序填充整数值。创建两个索引,分别称它们为
    i
    j
    ,用于分别迭代
    arr1
    arr2
    ,并将它们初始化为0。比较每个数组的前两个值:如果
    arr1[0]
    小于
    arr2[0]
    ,则增量
    i
    ,否则如果
    arr1[0]
    大于
    arr2[0]
    增量
    j
    ,则值相交,我们可以返回此值。一旦返回k个相交值,就可以停止迭代。在最坏的情况下,如果两组值之间没有交点,那么这将是i+j,O(n),并且我们必须迭代到每个数组的末尾

    下面是bash中的解决方案:

    #!/bin/bash
    #-------------------------------------------------------------------------------
    # Design an algorithm to find the k smallest integers in S1 ∩ S2 in O(n) time.
    #-------------------------------------------------------------------------------
    
    typeset -a arr1 arr2 arr_answer
    typeset -i array_size=20 k=5
    
    function populate_arrs {
        typeset -i counter=0
    
        while [[ ${counter} -lt ${array_size} ]]; do
            arr1[${counter}]=$((${counter} * 2))
            arr2[${counter}]=$((${counter} * 3))
    
            counter=${counter}+1
        done
    
        printf "%8s" "Set1: "; printf "%4d" ${arr1[*]}; printf "\n"
        printf "%8s" "Set2: "; printf "%4d" ${arr2[*]}; printf "\n\n"
    }
    
    
    function k_smallest_integers_main {
        populate_arrs
    
        typeset -i counter=0 i=0 j=0
        while [[ ${counter} -lt ${k} ]]; do
            if [[ ${arr1[${i}]} -eq ${arr2[${j}]} ]]; then
                arr_answer[${counter}]=${arr1[${i}]}
                counter=${counter}+1; i=${i}+1; j=${j}+1
            elif [[ ${arr1[${i}]} -lt ${arr2[${j}]} ]]; then
                i=${i}+1
            else
                j=${j}+1
            fi
        done
    
        printf "%8s" "Answer: "; printf "%4d" ${arr_answer[*]}; printf "\n"
    }
    
    k_smallest_integers_main
    
    输出:

      Set1:    0   2   4   6   8  10  12  14  16  18  20  22  24  26  28  30  32  34  36  38
      Set2:    0   3   6   9  12  15  18  21  24  27  30  33  36  39  42  45  48  51  54  57
    
    Answer:    0   6  12  18  24
    

    由于数组已按升序排序,因此可以使用2指针技术(几乎与mergesort中的merge过程相同)来完成此操作。散列是一种杀死苍蝇的方法。使用合并操作。@Yves:合并是使用一个稍微小一点的锤子来杀死苍蝇-这更好,但它仍然是一个锤子:-)不需要以任何方式合并列表,它可以在O(n)时间和O(1)空间内完成。当然,除非根据我的回答,您指的是“虚拟”合并操作,在这种情况下,我提前表示歉意。@paxdiablo:如果必须对结果进行进一步处理,则合并是强制性的,决不是过火。顺便说一句,用一个append替换你的打印就足够了,你会得到完全相同的算法。计数排序很可能是不合适的,因为它的复杂性不是O(n),而是O(n+m),其中m是键范围的大小。通常,m>>n.只是一个小小的吹毛求疵,
    O(i+j)
    似乎表明了对复杂性分析的根本误解。这只是
    O(n)
    ,最坏的情况。更准确的说法是,最坏的情况是
    i+j
    “步骤”,而不是在混合中引入大O。@paxdiablo谢谢,更正了,是不是有点像O(m+n)只是O(n),或者我在这里遗漏了什么?
      Set1:    0   2   4   6   8  10  12  14  16  18  20  22  24  26  28  30  32  34  36  38
      Set2:    0   3   6   9  12  15  18  21  24  27  30  33  36  39  42  45  48  51  54  57
    
    Answer:    0   6  12  18  24