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
添加到hashsetS2
中的每个e
,检查hashset中的e
在O(n)时间内是否存在
e
,则将e
添加到交叉点
k
整数我走对了吗?是的,你肯定走对了,但实际上根本不需要生成哈希表或额外的集。由于您的两个集合已经排序,您只需在它们之间运行一个索引/指针,查找公共元素 例如,要从两个集合中查找第一个公共元素,请使用以下伪代码:
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