这是一道比较经典的数组算法题。很多地方都有解法。当然蛮力肯定是不得分的。
我们来看一个数组A1,A2...An. 如果我们想得到Ai到Aj的和。我们可以求A1..Ai-1的和,然后再求A1到Aj的和。用这两个和相减,得到的就是Ai到Aj的和。因为A1到Aj的和包括A1到Ai-1,Ai到Aj。那么通过一次遍历数组我们就能够得到A1到An中任意数的和。但是我们要找的是子数组之和等于特定值。我们如何利用得到的结果呢?我们定义得到的结果为SUM1,SUM2...SUMn。那么用SUMm- 特定值S。如果能够在SUM数组中找到SUMn等于这个结果,SUMm-S=SUMn于是有SUMm-SUMn=S。根据我们前面的推倒,m到n求和的结果就是S。也就是说m到n就是我们要找的子数组。我们可以用一个二叉树表示SUMm-S的结果,然后每次遍历这个查找二叉树,看有没有找到SUMn期待的SUMm-S。如果没有,我们可以把SUMn-S也放入这个二叉树,继续SUMn+1的查找,知道找到匹配的结果。时间复杂度是nlogn。如果S值相对很小,我们还可以用一个0到S的数组,然后通过将SUMm-S放入数组指定下标。用这个方法,我们可以在O(1)的时间复杂度完成一次查找。所以整体的时间复杂度可以减小到O(n)。但是缺点是空间复杂度的增加。
struct Node{
Node* left;
Node* right;
int value;
int pos;
}
bool InsertNode(Node* root, int value, int pos, int& start, int& end)
{
if(NULL == root)
{
return false;
}
if(root->value = value)
{
start = pos;
end = pos;
return true;
}
while(NULL != root)
{
if(root->value == value)
{
if(root->pos < pos)
{
start = root->pos;
end = pos;
}
else
{
end = root->pos;
start = pos;
}
return true;
}
else if(root->value < value)
{
if(root->right)
{
root = root->right;
}
else
{
Node* newNode = new Node;
newNode->pos = pos;
newNode->value = value;
root->right = newNode;
return false;
}
}
else
{
if(root->left)
{
root = root->left;
}
else
{
Node* newNode = new Node;
newNode->pos = pos;
newNode->value = value;
root->left = newNode;
return false;
}
}
}
return false;
}
void FindSubArrayEqValue(int* A, int length, int S,int& start, int& end)
{
if(length < 1 || NULL == A)
{
start = -1;
end = -1;
return;
}
if(length ==1)
{
start = 0;
end = 0;
}
int* SUM = new int[length];
SUM[0] = A[0];
for(int i = 1; i < length; ++i)
{
SUM[i] = SUM[i - 1] + A[i];
}
Node* root = new Node;
root->pos = -1;
root->value = 0;
for(int i = 0; i < length; ++i)
{
if(InsertNode(root,SUM[i] - S, i,start,end))
{
return ;
}
}
ClearTree(root);
delete []SUM;
}
下面有一个更为复杂的思考题,如何找到子数组的和最接近特定值?
明天给出答案:)