编辑 - 我删除了所有不必要的上下文解释 - 太罗嗦,最终与问题无关。总之,我在构建平衡 KD 树的过程中对坐标数组进行了分区(更多信息请参见维基百科文章的构造部分。我实际上有 n 个项目的 k 个并行数组,每个项目都必须通过相同的比较进行分区)
这不是家庭作业——我写这个问题的目的是为了确保传达所有的细微差别。
给定排序数组:
int[] ints = { 0, 1, 2, 3, 4, 5, 6 };
//this one is important - my current solution fails on this
int[] ints2 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
请注意,由于一位同事的澄清,这些数组的所有保证element[n]
都是小于或等于element[n+1]
.
对它们的成功操作会将它们分成两个子数组L
和R
(如下所示):
/*ints == */ { 1, 3, 5, 0, 2, 4, 6 }
/*|> L <| |> R <|*/
/*ints2 == */ { 1, 3, 5, 7, 9, 0, 2, 4, 6, 8 }
/*|> L <| |> R <|*/
L
包含奇数和R
偶数,同时保留这些子数组中这些元素的原始排序顺序。
理想情况下,该函数不会对元素进行重新排序(已经提前执行了冗长的排序操作)并且它不会使用临时数组。我相信这意味着我正在寻找 O(N) 复杂性和 O(1) 内存。
该函数可以提供每个子数组的开始和结束元素 - 即调用者可以提前知道有多少项目将落在左侧/右侧(可能通过提前扫描数组的奇数/偶数)。 在现实中编辑它就像一个数组一样开始;所以在没有这些值的情况下可以工作的解决方案是好的,因为否则如果需要初始通过,完整的解决方案实际上最多只能是 O(2n) 复杂度。
这是我目前的尝试所在 - 我已经更新了它并从原始帖子中评论了它。
public void DivideSubArray(int[] array, int leftStart, int leftCount,
int rightStart, int rightCount)
{
int currentLeft = leftStart, currentRight = rightStart;
int leftCounter = leftCount;
int temp;
int readahead;
while (leftCounter != 0) {
if ((array[currentLeft] % 2) == 0)
{
//remember the element we swap out
temp = array[currentRight];
//Set as next item on the right. We know this is the next lowest-sorted
//right-hand item because we are iterating through an already-sorted array
array[currentRight++] = array[currentLeft];
// * read ahead to see if there are any further elements to be placed
// * on the left - move them back one by one till there are no more.
readahead = currentLeft + 1;
while ((array[readahead] % 2) != 0)
{
array[currentLeft++] = array[readahead++];
leftCounter--;
}
//Now write the swapped-out item in, but don't increment our currentLeft.
//The next loop will check if the item is in the correct place.
array[currentLeft] = temp;
}
else //this item is already in the correct place
{
currentLeft++;
leftCounter--;
}
}
}
当调用如下:
int numOdd = ints.Count(i => (i % 2) == 1);
DivideSubArray(ints, 0, numOdd, numOdd, ints.Length - numOdd);
ints
它为(和许多其他数组)生成预期的数组,但不是ints2
:
{ 1, 5, 3, 7, 9, 0, 2, 6, 4, 8 }
所以它正确分区 - 但交换3,5
和6,4
. 我明白为什么:因为在第一个循环5
中被交换到左边,然后2
被传播,因为算法说这5
是奇怪的并且应该保留。我写了一个可以修复它的决策树,但是在遵循它几个循环之后,它推断出解决方案是递归的。
我正在努力了解如何在不在子数组中运行更多排序操作或创建临时列表/数组作为工作区的情况下解决这个问题。当然,虽然排序可能会增加复杂性,但会保留内存需求;如果事实证明它是最快的解决方案,那么使用它是有意义的。
您可以在我的回答下看到我当前最快(在运行时)和最佳内存解决方案。作为衡量标准 - 上述尝试不仅会产生不正确的结果,而且需要的时间是我答案中代码的 3 倍。
我觉得必须有一种简单的方法来利用单个“备用”变量来交换项目 - 我只是看不到它 - 我希望 SO 集体大脑会:)
当然,如果答案是“不”,那就这样吧。