我已经尝试解决了很长时间,但似乎无法解决。
问题如下:
给定一个数组n个数字,其中的所有数字都出现两次,除了一个(仅出现一次)之外,找到仅出现一次的数字。
现在,我已经在网上找到了许多解决方案,但是没有一个可以满足该问题的其他约束。
解决方案应:
因此,无法使用XOR运算符尝试类似https://stackoverflow.com/a/4772568/7774315的操作,因为我们没有XOR运算符。由于每个数字中的位数大约为O(log(n)),因此尝试使用普通算术(逐位)实现XOR运算符将需要大约O(log(n))动作,这将为我们提供一个总体O(nlog(n))的解。
我要解决的最接近的问题是,如果我有办法在线性时间内获得数组中所有唯一值的总和,我可以从总和中减去该总和的两倍,以获得(负数)仅出现一次,因为如果出现两次的数字是{a1,a2,....,ak}并且出现一次的数字是x,则总和为
总和= 2(a1 + ... + ak)+ x
据我所知,集合是使用哈希表实现的,因此使用它们查找所有唯一值的总和是不好的。
答案 0 :(得分:6)
让我们想象一下,有一种方法可以找到线性时间的精确中值并划分数组,以便所有较大的元素都在一侧,较小的元素在另一侧。通过期望元素数量的奇偶性,我们可以确定目标元素在哪一侧。现在在我们确定的部分中递归执行此例程。由于该部分每次都减半,因此遍历的元素总数不能超过O(2n)= O(n)。
答案 1 :(得分:2)
问题中的关键要素似乎就是这个:
数组中每个数字的位数约为O(log(n))。
问题是这个线索有点模糊。
第一种方法是考虑最大值为O(n)。然后可以在O(n)操作和O(n)存储器中执行计数排序。
它将包括找到最大值MAX,设置整数数组C [MAX]并直接执行经典计数排序
C[a[i]]++;
在数组C[]
中寻找奇数将提供解决方案。
第二种方法,我认为更有效的方法是设置一个大小为n
的数组,每个元素都由一个未知大小的数组组成。然后,一种几乎计数的排序将包含在:
C[a[i]%n].append (a[i]);
要找到唯一元素,我们必须找到一个奇数大小的子数组,然后检查该子数组中的元素。
每个子阵列的最大大小k
将约为2 *(MAX / n)。根据提示,该值应该非常低。处理这个子数组的复杂度为O(k),例如,通过对b[j]/n
执行计数排序,所有元素的模n相等。
我们可以注意到,实际上,这等效于执行一种临时哈希。
全局复杂度为O(n + MAX / n)。
答案 2 :(得分:0)
只要您处理的大小为O(log n)
的整数,这应该可以解决问题。这是草绘@גלעד ברקן answer(包括@OneLyner注释)的算法的Python实现,其中,中位数被平均值或中值代替。
def mean(items):
result = 0
for i, item in enumerate(items, 1):
result = (result * (i - 1) + item) / i
return result
def midval(items):
min_val = max_val = items[0]
for item in items:
if item < min_val:
min_val = item
elif item > max_val:
max_val = item
return (max_val - min_val) / 2
def find_singleton(items, pivoting=mean):
n = len(items)
if n == 1:
return items[0]
else:
# find pivot - O(n)
pivot = pivoting(items)
# partition the items - O(n)
j = 0
for i, item in enumerate(items):
if item > pivot:
items[j], items[i] = items[i], items[j]
j += 1
# recursion on the partition with odd number of elements
if j % 2:
return find_singleton(items[:j])
else:
return find_singleton(items[j:])
以下代码仅用于对随机输入进行一些完整性检查:
def gen_input(n, randomize=True):
"""Generate inputs with unique pairs except one, with size (2 * n + 1)."""
items = sorted(set(random.randint(-n, n) for _ in range(n)))[:n]
singleton = items[-1]
items = items + items[:-1]
if randomize:
random.shuffle(items)
return items, singleton
items, singleton = gen_input(100)
print(singleton, len(items), items.index(singleton), items)
print(find_singleton(items, mean))
print(find_singleton(items, midval))
对于对称分布,中值与平均值或中值重合。
对于条目位数的log(n)要求,一个
可以表明,任何任意子采样都不能完全倾斜以提供超过log(n)
个递归。
例如,考虑k = 4的k = log(n)位的情况,并且只有正数,最坏的情况是:[0, 1, 1, 2, 2, 4, 4, 8, 8, 16, 16]
。在此,按均值进行枢转将使输入一次减少2,从而导致k + 1个递归调用,但是在输入中添加任何其他对不会增加递归调用的数量,但会增加输入的大小。
(编辑以提供更好的解释。)
答案 3 :(得分:0)
这里是גלעדברקן勾勒出的想法的(未优化)实现。 我正在使用Median_of_medians来获取足够接近中值的值,以确保在最坏情况下的线性时间。
NB:实际上,这仅使用比较,并且只要比较和副本的计数为O(1),无论整数的大小为O(n)。
def median_small(L):
return sorted(L)[len(L)//2]
def median_of_medians(L):
if len(L) < 20:
return median_small(L)
return median_of_medians([median_small(L[i:i+5]) for i in range(0, len(L), 5)])
def find_single(L):
if len(L) == 1:
return L[0]
pivot = median_of_medians(L)
smaller = [i for i in L if i <= pivot]
bigger = [i for i in L if i > pivot]
if len(smaller) % 2:
return find_single(smaller)
else:
return find_single(bigger)
此版本需要O(n)额外的空间,但可以使用O(1)来实现。