查找列表中出现奇数次的元素
Find Elements occurring odd number of times in a list
假设我们需要在 O(N) 时间和 O(1) space 复杂度内找到在排序列表中出现奇数次的所有元素。
ls = [1,2,2,3,3,3,4,5,5,6,6,6,6,6]
output = [1,3,4,6]
我们不会return一个新的列表,也许会覆盖现有的
我有一种使用哈希技术的方法,但它会导致复杂度为 O(N) space。
我尝试过使用异或进行位运算,但我无法解决问题。
遍历列表,计算当前项与前一项相同的情况,如果不同且计数为奇数,则向列表的开头覆盖。
此解决方案覆盖现有列表而不是分配新列表,并且具有 O(N) 时间复杂度。因为新列表会更短,所以我们必须 pop
从它的末尾开始剩余的项目。 (我们通常会使用 ls = ls[position:]
进行拼接,但这会分配一个新列表,这是不允许的。)
def keep_odd_elements(ls):
count = 0
write_position = 0
previous = object()
for item in ls:
if item == previous:
count += 1
else:
# Write the odd-counted numbers towards the start of the list
if count % 2:
ls[write_position] = previous
write_position += 1
count = 1
previous = item
if count % 2:
ls[write_position] = previous
write_position += 1
# Remove the leftover items at the end of the list
for _ in range(write_position, len(ls)):
ls.pop()
ls = [1,2,2,3,3,3,4,5,5,6,6,6,6,6]
keep_odd_elements(ls)
print(ls) # [1, 3, 4, 6]
如果我们取消不分配新列表的要求,那么我们可以写得更优雅:
def get_odd_elements(ls):
count = 0
for a, b in zip([object()] + ls, ls + [object()]):
if a == b:
count += 1
else:
if count % 2:
yield a
count = 1
print(list(get_odd_elements([1,2,2,3,3,3,4,5,5,6,6,6,6,6])))
这是在 O(n) 中运行并使用 O(1) 辅助内存的 C++ 代码。
这段代码背后的想法很简单,我们从左边开始计算一个数字出现的次数
x
直到我们到达一个不等于 x
的数字 y
,因为我们的输入是排序的,它保证 x
永远不会出现在 y
之后。因此,检查 x
的出现次数是否为奇数就足够了。
请注意,如果数组未排序,则可以证明,你不能比 O(nlogn)
做得更好
//Find odd numbers in given sorted list
#include <stdio.h>
int main(){
int ArrayLength;
scanf("%d",&ArrayLength);
int array[ArrayLength];
//Read input sorted array
for ( int i = 0; i < ArrayLength; i++)
scanf("%d",&array[i]);
// Find numbers with odd occurrence
int PreviousLocalOdd=array[0];
int LocalOdd=1;
for ( int i = 1; i < ArrayLength; i++)
{
if (array[i]==PreviousLocalOdd)
LocalOdd++;
if(array[i]!= PreviousLocalOdd)
{
if(LocalOdd % 2==1)
printf("%d",array[i-1]);
LocalOdd=1;
PreviousLocalOdd=array[i];
}
}
if(LocalOdd % 2==1)
printf("%d ",array[ArrayLength-1]);
return 0;
}
假设我们需要在 O(N) 时间和 O(1) space 复杂度内找到在排序列表中出现奇数次的所有元素。
ls = [1,2,2,3,3,3,4,5,5,6,6,6,6,6]
output = [1,3,4,6]
我们不会return一个新的列表,也许会覆盖现有的
我有一种使用哈希技术的方法,但它会导致复杂度为 O(N) space。
我尝试过使用异或进行位运算,但我无法解决问题。
遍历列表,计算当前项与前一项相同的情况,如果不同且计数为奇数,则向列表的开头覆盖。
此解决方案覆盖现有列表而不是分配新列表,并且具有 O(N) 时间复杂度。因为新列表会更短,所以我们必须 pop
从它的末尾开始剩余的项目。 (我们通常会使用 ls = ls[position:]
进行拼接,但这会分配一个新列表,这是不允许的。)
def keep_odd_elements(ls):
count = 0
write_position = 0
previous = object()
for item in ls:
if item == previous:
count += 1
else:
# Write the odd-counted numbers towards the start of the list
if count % 2:
ls[write_position] = previous
write_position += 1
count = 1
previous = item
if count % 2:
ls[write_position] = previous
write_position += 1
# Remove the leftover items at the end of the list
for _ in range(write_position, len(ls)):
ls.pop()
ls = [1,2,2,3,3,3,4,5,5,6,6,6,6,6]
keep_odd_elements(ls)
print(ls) # [1, 3, 4, 6]
如果我们取消不分配新列表的要求,那么我们可以写得更优雅:
def get_odd_elements(ls):
count = 0
for a, b in zip([object()] + ls, ls + [object()]):
if a == b:
count += 1
else:
if count % 2:
yield a
count = 1
print(list(get_odd_elements([1,2,2,3,3,3,4,5,5,6,6,6,6,6])))
这是在 O(n) 中运行并使用 O(1) 辅助内存的 C++ 代码。
这段代码背后的想法很简单,我们从左边开始计算一个数字出现的次数
x
直到我们到达一个不等于 x
的数字 y
,因为我们的输入是排序的,它保证 x
永远不会出现在 y
之后。因此,检查 x
的出现次数是否为奇数就足够了。
请注意,如果数组未排序,则可以证明,你不能比 O(nlogn)
做得更好 //Find odd numbers in given sorted list
#include <stdio.h>
int main(){
int ArrayLength;
scanf("%d",&ArrayLength);
int array[ArrayLength];
//Read input sorted array
for ( int i = 0; i < ArrayLength; i++)
scanf("%d",&array[i]);
// Find numbers with odd occurrence
int PreviousLocalOdd=array[0];
int LocalOdd=1;
for ( int i = 1; i < ArrayLength; i++)
{
if (array[i]==PreviousLocalOdd)
LocalOdd++;
if(array[i]!= PreviousLocalOdd)
{
if(LocalOdd % 2==1)
printf("%d",array[i-1]);
LocalOdd=1;
PreviousLocalOdd=array[i];
}
}
if(LocalOdd % 2==1)
printf("%d ",array[ArrayLength-1]);
return 0;
}