子数组的和等于特定值

这是一道比较经典的数组算法题。很多地方都有解法。当然蛮力肯定是不得分的。

我们来看一个数组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;
}


下面有一个更为复杂的思考题,如何找到子数组的和最接近特定值?

明天给出答案:)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值