在 O(1) 空间和 O(n) 时间中重新排序的布尔数组
Posted
技术标签:
【中文标题】在 O(1) 空间和 O(n) 时间中重新排序的布尔数组【英文标题】:Boolean array reordering in O(1) space and O(n) time 【发布时间】:2015-06-25 18:26:38 【问题描述】:问题取自Elements of Programming Interviews:
给定一个包含 n 个对象且具有布尔值键的数组 A,重新排序该数组以使键为 false 的对象首先出现。 键为 true 的对象的相对顺序不应改变。 使用 O(1) 额外空间和 O(n) 时间。
我做了以下,它保留了键为 true 的对象的相对顺序并使用了 O(1) 额外空间,但我相信它的时间复杂度是 O(n*n!)。
public static void rearrangeVariant4(Boolean[] a)
int lastFalseIdx = 0;
for (int i = 0; i < a.length; i++)
if (a[i].equals(false))
int falseIdx = i;
while (falseIdx > lastFalseIdx)
swap(a, falseIdx, falseIdx-1);
falseIdx--;
lastFalseIdx++;
有人知道如何在 O(n) 时间内解决它吗?
【问题讨论】:
密切相关,Dutch National Flag Problem. 【参考方案1】:boolean array[n]; // The array
int lastTrue = n;
for (int i = n-1; i >= 0; --i)
if (array[i])
swap(array[--lastTrue], array[i]);
在每次迭代之后,lastTrue
之后的所有元素都为真。没有两个真正的元素被交换,因为如果在i
和lastTrue
之间有一个真正的元素,它就会已经遇到并移到lastTrue
后面。这在O(n)
时间和O(1)
内存中运行。
【讨论】:
非常好!干净高效! OP 只要求保留真实值之间的相对顺序。 @Ricky Don't fall into this trap。在任何情况下,我们都在谈论 Java,因此整数(和数组大小)被限制为 32 位,因此假设单个数字的操作和内存恒定时间更有用且更简单。 @Ricky 是的,这完全取决于您要使用的计算模型。事实证明,对于多种算法来说,假设单个整数占用 O(1) 空间和 O(1) 时间来进行简单的算术运算是最有用的。因此,对于许多问题,假设reduce(arr, (t, e) -> t + e)
为 O(n) 非常好,虽然是的,您也可以使用 O(nm) 且 m 为位大小 - 对于某些问题,这确实会产生很大的不同。跨度>
如果递减不是内联发生的,我会认为这是一种改进。 =/ 少思考,少混乱。【参考方案2】:
让数组有基于 0 的索引,并让它有 n
元素。然后你可以执行以下操作(下面的伪代码)
// A[] is your array
i = 0
k = 0
for i from 0 to n-1
if A[i] is true
swap A[i] and A[k]
increment k
时间复杂度为O(n)
,额外空间仅用于两个变量i
和j
,因此内存为O(1)
。这样顺序被保留在 false 和 true 值之间。 (这个方法是把真实的放在第一位,你可以根据你的要求改变它)。
【讨论】:
是的,顺序被保留,但数组以真值而不是假开始。 (也许在 O(n) 时间内反转它会奏效吗?) 只需从前向后而不是从前向后。 或者只测试假值而不是真值。将if A[i] is true
更改为if A[i] is false
@ubica:不。这将保留假键元素的顺序,同时将它们放在开头。您想要的是保留真键元素的顺序,同时将它们放在最后。
将true
替换为false
。这不是重新排序一些真实值吗?你需要插入而不是交换,不是吗?【参考方案3】:
观察到固定 k 的 2k 是 O(1),而 2n 是 O(n)。构造第二个数组,将源数组中的元素复制到目标数组,在一端添加键为false
的元素,在另一端添加键为true
的元素。您可以扫描一次数组以找出边界必须在哪里。
【讨论】:
OP 需要 O(1) 额外空间。如何在 O(1) 空间中创建一个额外的数组。 k 指的是什么?【参考方案4】:
Java
代码 - 如果您使用的是 Boolean[]
- 对象:
时间 - O(1),空间 - O(1)
public static <T> void swap(T[] a, int i, int j)
T t = a[i];
a[i] = a[j];
a[j] = t;
时间 - O(N),空间 - O(1)
public static Boolean[] getReorderBoolObjects(Boolean[] array)
int lastFalse = 0;
for (int i = 0; i < array.length; i++)
if (!array[i])
swap(array, lastFalse++, i);
return array;
Spock
测试用例:
def "reorder bools - objects"()
given:
Boolean[] b = [false, true, true, true, false, true]
when:
getReorderBoolObjects(b)
then:
b == [false, false, true, true, true, true]
Java
代码 - 如果您使用的是 boolean[]
- 原语:
时间 - O(N),空间 - O(1)
public static boolean[] getReorderBoolPrimitives(boolean[] array)
int falseCount = 0;
for (final boolean bool : array)
if (!bool)
falseCount++;
for (int i = 0; i < array.length; i++)
array[i] = i >= falseCount;
return array;
Spock
测试用例:
def "reorder bools - primitives"()
given:
boolean[] b = [false, true, true, true, false, true]
when:
getReorderBoolPrimitives(b)
then:
b == [false, false, true, true, true, true]
【讨论】:
数组包含带有布尔字段的对象。需要将整个对象交换到新位置,而不仅仅是真值。 "给定一个包含布尔值键的 n 个对象的数组 A" @EdwardDoolittle 你说得对,我读的问题标题是boolean[]
。
它不保留顺序。我知道布尔值太原始了,无法看到不保留真实元素顺序的后果。【参考方案5】:
public static void rearrange(boolean[] arr)
int invariant = arr.length-1;
for (int i = arr.length -1; i >= 0; i --)
if ( !arr[i] )
swap( arr,i,invariant);
invariant--;
private static void swap(boolean arr[] , int from ,int to)
boolean temp = arr[from];
arr[from]=arr[to];
arr[to]=temp;
【讨论】:
虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高答案的长期价值。以上是关于在 O(1) 空间和 O(n) 时间中重新排序的布尔数组的主要内容,如果未能解决你的问题,请参考以下文章