关于我的算法决赛的最后一个问题让我在过去一个月里疯狂。这是一个问题:
你有一个数组
A[0...n]
,编写一个在O(n)中运行的算法(在“正确的”伪代码中),该算法可以确定该数组是否已经相对于某个索引k
进行了分区。如果是,请找k
;如果没有,则返回-1;
通过Partition
澄清:
对于
e
中的每个元素A[0...n]
,如果e < A[k]
将e
放置在A[k]
的“左侧”,则将e
添加到A[k]
A = [4 2 5 3 7 4 2 6 8 4 1
的“权利”。
所以分区数组的例子(w.r.t. k = 11):
10 10 20 11 15 13 28 99 11]
的 10myAlgo(A) -> (11)
然后
A = [10, 20, 30, 40, 11,
或
, 150, 101, 125]
的 100myAlgo(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 >:/
但是,毫不奇怪,我的输出是这样的:
isPart
我真的喜欢能够在今晚睡觉,所以任何提示/提示/想法/等都会非常非常感激。
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;
}
答案 0 :(得分:20)
O(n)
时间+空间解决方案是拥有两个数组max
和min
。
max[i] = max{arr[0],arr[1],...,arr[i]}
min[i] = min{arr[i],arr[i+1],...,arr[n-1]}
请注意,您可以使用线性时间创建两个数组。
拥有这些数组之后,您需要查找是否存在索引k
,以便:
arr[k] >= max[k-1] && arr[k] <= min[k+1]
这也可以在线性时间内完成
这样做有效,因为如果上述情况成立,则k
之后的每个元素都保证更高或等于arr[k]
,并且之前的每个元素都更低或等于arr[k]
,这几乎是分区的定义。
答案 1 :(得分:1)
有趣的问题
在我看来,必须有可能解决这个问题,而不必诉诸额外的缓冲空间。
我们知道如果有一个枢轴元素,那么
由此我们知道
这是一个特例
使用这样的理由,我们应该能够以递归的方式回到家中。在枢轴位置,如果有的话。
的伪代码:
Set highest value found on low side to value of first element
Set lowest value found on high side to value of last element
Set low index to first element
Set high index to last element
repeat
increment low index
if low index >= array length -> fail
if value at new low index > highest so far on the low side
set new highest-on-low-side value
if new value greater than lowest value so far on right side,
set low index back to what it was and mark it as stuck
set highest-on-low-side value back to what it was
decrement high index
if high index < 0 -> fail
if value at new high index < lowest so far on the high side
set new lowest-on-high-side value
if new value less than the highest value so far on the left side,
set high index back to what it was and mark it as stuck
set lowest-on-high-side value back to what it was
until both low and high index is stuck or until low index >= high index
if low index = high index
pivot position = low index
else
failure
这是一个实际的Pascal实现,我曾经用一些测试输入来简要验证这个想法,但我现在还没有时间进行全面的验证。
function PivotIndex(a: array of integer): Integer;
var
HighestValueOnLeftSide: Integer;
LowestValueOnRightSide: Integer;
LowIndex: Integer;
HighIndex: Integer;
LowStuck, HighStuck: Boolean;
begin
HighestValueOnLeftSide := -1;
LowestValueOnRightSide := MaxInt;
LowIndex := -1;
HighIndex := length(a);
LowStuck := False;
HighStuck := False;
repeat
if not LowStuck then begin
inc(LowIndex);
if LowIndex >= length(A) then begin
Result := -1;
exit;
end;
if A[LowIndex] > HighestValueOnLeftSide then
if A[LowIndex] > LowestValueOnRightSide then begin
LowStuck := True;
dec(LowIndex);
end else
HighestValueOnLeftSide := A[LowIndex];
end;
if not HighStuck then begin
dec(HighIndex);
if HighIndex < 0 then begin
Result := -1;
exit;
end;
if A[HighIndex] < LowestValueOnRightSide then
if A[HighIndex] < HighestValueOnLeftSide then begin
HighStuck := True;
inc(HighIndex);
end else
LowestValueOnRightSide := A[HighIndex];
end;
until LowStuck and HighStuck or (LowIndex >= HighIndex);
if LowIndex = HighIndex then
Result := LowIndex
else
Result := -1;
end;
我确信这可以变得更加优雅和高效,但是如果您发现它有任何直接问题,请告诉我。