【发布时间】:2013-08-08 14:58:49
【问题描述】:
在过去的一个月里,我的算法决赛的最后一个问题一直让我发疯。问题来了:
你有一个数组
A[0...n],写一个算法(在“适当的”伪代码中)在 O(n) 中运行,它可以确定这个数组是否已经相对于某个索引k进行了分区,如果是,找到k;如果不是则返回-1;
澄清一下,Partition:
对于
A[0...n]中的每个元素e,如果e < A[k]将e放在A[k]的“左侧”,否则将e放在A[k]的“右侧”。
一个分区数组的例子(w.r.t. k = 11):
A = [4 2 5 3 7 4 2 6 8 4 11010 10 20 11 15 13 28 99 11]
然后
myAlgo(A) -> (11)
或
A = [10, 20, 30, 40, 11,100, 150, 101, 125]
然后
myAlgo(A) -> (5)
但不是:
A = [10, 20, 30, 40, 5]
myAlgo(A) -> (-1)
我的第一个想法(非常天真)太糟糕了,我简直无法用语言表达。基本上,它无意中检查了数组是否已排序并从中间拉出一个相当随机的值。
我的下一个想法是扫描列表并首先检查以找到我在达到递减数字之前达到的最高数字并将所有这些数字排除在外......基本上保持一个最大值和一个最小值,如果事情落在外面然后将我可能的分区索引转移到我的子集的末尾。
这是我尝试(非常非常糟糕地)实现它的地方(使用测试用例):
int myAlgo(const int* A, int n);
int main() {
const int A[] = {10, 20, 30, 40, 11, 100, 150, 101, 125};
int index;
if((index = myAlgo(A, 9)) != -1) {
printf("A[%d] = %d", index, A[index]);
}
else {
printf("Not Partitioned >:/");
}
return 0;
}
int myAlgo(const int* A, int n) {
// the index of the smallest possible number in the remainder of the list
int minIdx = 0;
// the index of the largest number we've encountered
int maxIdx = 0;
// index of possible partition "center"
int kIdx = 0;
bool isPart = false;
for(int i=0; i < n; ++i) {
if( A[maxIdx] <= A[i] ) {
maxIdx = i;
if(isPart == false) { kIdx = i; minIdx = i;} // if we flipped then this is a good time to grab a partitioner index
isPart = true;
}
else { isPart = false; minIdx = i; }
printf("A[%d] = %d <==> A[%d]: %d : %c\n", maxIdx, A[maxIdx], i, A[i], (isPart?'T':'F'));
if( A[minIdx] > A[i] ) { isPart = false; }
printf("A[%d] = %d <==> A[%d]: %d : %c\n", minIdx, A[minIdx], i, A[i], (isPart?'T':'F'));
}
printf("A[%d] = %d : %c\n\n", kIdx, A[kIdx], (isPart?'T':'F'));
// We gotta check this to make sure it is a valid list...
if(isPart) return kIdx;
else return -1;
}
但是,毫不奇怪,我的输出是这样的:
A[0] = 10 <==> A[0]: 10 : T
A[0] = 10 <==> A[0]: 10 : T
A[1] = 20 <==> A[1]: 20 : T
A[0] = 10 <==> A[1]: 20 : T
A[2] = 30 <==> A[2]: 30 : T
A[0] = 10 <==> A[2]: 30 : T
A[3] = 40 <==> A[3]: 40 : T
A[0] = 10 <==> A[3]: 40 : T
A[3] = 40 <==> A[4]: 11 : F
A[4] = 11 <==> A[4]: 11 : F
A[5] = 100 <==> A[5]: 100 : T
A[5] = 100 <==> A[5]: 100 : T
A[6] = 150 <==> A[6]: 150 : T
A[5] = 100 <==> A[6]: 150 : T
A[6] = 150 <==> A[7]: 101 : F
A[7] = 101 <==> A[7]: 101 : F
A[6] = 150 <==> A[8]: 125 : F
A[8] = 125 <==> A[8]: 125 : F
A[5] = 100 : F <-- The index is right... but isPart is wrong
Not Partitioned >:/
我真的希望今晚能够入睡,因此非常非常感谢任何提示/提示/想法/等。
哇! @Amit 帮我解决了我的问题,这是我的更新功能:
int partIdx2(const int* A, int n) {
int* max = malloc(n * sizeof(int));
int* min = malloc(n * sizeof(int));
for(int i=0; i < n; i++)
{
if(i==0) {
max[i] = A[i];
min[n - 1] = A[n-1];
}
else {
max[i] = MAX(max[i-1], A[i]);
min[n - 1 - i] = MIN(min[n - 1 - i + 1], A[n - 1 - i]);
}
}
for(int i=1; i < n-1; i++) {
if(A[i] >= max[i-1] && A[i] <= min[i+1]) {
free(max);
free(min);
return i;
}
}
free(max);
free(min);
return -1;
}
【问题讨论】:
-
我真的很喜欢这个解决方案。除非需要,否则我不喜欢分配空间,而且我对这个问题的尝试也没有 (see it live with your problem sets)。不知道我是否错过了什么,但这真是令人头疼。
-
@WhozCraig:我一开始就试图这样做,但我一直在试图找到我的支点可能达到的绝对最小值时遇到了困难……使用 Amit 解决方案中的一些见解,我会可能让我的第一个想法再试一次。
-
@dalle,如果枢轴是已知的,这将很有用,但要使用
is_partitioned(...)解决这个问题(请注意,我们是 A. 检查一般分区性和 B. 返回枢轴索引)我们必须为列表中的每个元素调用它(我们的谓词是x < A[pivot])......这将导致 O(n^2) 复杂度。