在数组中查找平衡点
Finding the balance point in an array
这个问题来自一个很棒的YouTube频道,给出了可以在采访中提出的问题。
它基本上与在数组中找到平衡点有关。这里有一个例子可以最好地解释它;{1,2,9,4,-1}.在这里,因为 sum(1+2)=sum(4+(-1)) 使 9 成为平衡点。在没有检查答案的情况下,我决定实现该算法,之前想问是否可以采取更有效的方法;
- 对数组 O(n) 中的所有元素求和
- 得到总和 O(1) 的一半
- 从左开始扫描数组,当 sumleft 大于一般总和的一半时停止。O(n)
- 对权利做同样的事情,以获得总和的权利。O(n)。
- 如果 sumleft 等于 sumright 返回 arr[size/2] 否则返回 -1
我问是因为这个解决方案毫不费力地出现在我的脑海中,提供了 O(n) 运行时间。如果这个解决方案是真的,是否可以开发,或者如果不是真的,是否有任何替代方法?
你的算法不好(反例:1 -1 1 0 1 -1 1
),好的解决方案是计算数组的部分和(这样你就可以计算数组每个单元格的 O(1) 中的sumleft
和sumright
),然后(或者同时,如果您已经知道全局总和)在你的数组中搜索一个单元格,这样sumleft = sumright
O(n)。
数组A
的部分和为
[A[0], A[0]+A[1], A[0]+A[1]+A[2], …, A[0]+A[1]+A[2]+…+A[n-1]]
例:
A=[5,2,3,1,4,6]
partial sum = [5,7,10,11,15,21]
使用此数组,您可以计算sumleft[i]=partial_sum[i-1]
和sumright[i]=partial_sum[n-1]-partial_sum[i]
起色:
如果存储所有partial_sum数组,则首先计算全局总和,然后仅计算当前索引的部分总和,则只能使用 O(1) 额外空间而不是 O(n) 额外空间。
基本上先将所有数字相加。这将是一个 O(n) 操作。然后从数组的开头开始一次从数组中减去一个元素,直到 upper
== lower
。因此,总订单将为 O(n)。
int BalancePoint(int a[], int begin, int end) // find index of an array (balance point) such that sum of all elements before the index = sum of all elements after it; else return -1
{
if(!a) return -1;
else if(begin == end) return begin;
long long upper = 0;
long long lower = 0;
for(int i = begin; i <= end; ++i)
{
upper += *(a+i);
}
for(int j = begin; j <= end; ++j)
{
upper -= *(a+j);
if(upper == lower) return j;
lower += *(a+j);
}
return -1;
}
使用 STL
int BalancePointSTL( const vector<int> &A ) // find index of an array (balance point) such that sum of all elements before the index = sum of all elements after it; else return -1
{
if(A.empty()) return -1;
long long upper = 0;
long long lower = 0;
for(unsigned int i = 0; i <= A.size(); ++i)
{
upper += A[i];
}
for(unsigned int j = 0; j < A.size(); ++j)
{
upper -= A[j];
if(upper == lower) return j;
lower += A[j];
}
return -1;
}
以下将具有更好的最坏情况性能,但要进行一些if-else
比较
int BalancePoint2(int a[], int begin, int end) // Better worst case senario by factor of 2
{
if(!a) return -1;
else if(begin == end) return begin;
long long upper = 0;
long long lower = 0;
int mid = (end-begin)/2;
for(int i = begin; i < mid; ++i)
{
lower += *(a+i);
}
for(int i = mid+1; i <= end; ++i)
{
upper += *(a+i);
}
if(upper == lower) return mid;
else if(lower < upper)
{
lower += *(a+mid);
for(int i= mid + 1 ; i <= end ; ++i)
{
upper -= *(a + i);
if(upper == lower) return i;
lower += *(a + i);
}
}
else {
upper += *(a + mid);
for(int i = mid - 1; i >=begin; --i)
{
lower -= *(a + i);
if(upper == lower) return i;
upper += *(a + i);
}
}
return -1;
}
我实际上有 2 个起点,一个在最左边的点 (leftLoc),一个在最右边的点 (rightLoc)。 保持一个总和左和总和右数。
leftLoc = 0;
rightLoc = (n - 1);
sumRight = array[rightLoc];
sumLeft = array[leftLoc];
while(leftLoc < rightLoc){
if(sumRight > sumLeft){
leftLoc++;
sumLeft += array[leftLoc];
}else{
rightLoc--;
sumRight += array[rightLoc];
}
}
if( (sumRight + array[rightLoc - 1]) == sumLeft ){
return rightLoc--;
}else if( (sumLeft + array[leftLoc + 1]) == sumRight){
return leftLoc++;
}else{
// return floating point number location in the middle of the 2 locations
}
同时跟踪总共移动了多少仓位 O(n)
您可能会发现您的平衡点是最终点中间的浮点数(一旦它们位于彼此相邻的整数位置)。
这甚至应该适用于负数示例。 也许我缺少一些细粒度的细节,但是这个主题的一些变化应该会导致您采用 O(n) 运行时算法。
您正在寻找质心或质心。在纯 Python 中:
def centroid(input_list):
idx_val_sum = 0.0
val_sum = 0.0
for idx,val in enumerate(input_list):
idx_val_sum += idx*val
val_sum += val
return idx_val_sum/float(val_sum)
它是 O(n),如果非整数结果格式不正确,您可以通过模检查来拒绝它们:
def integer_centroid(input_list):
idx_val_sum = 0.0
val_sum = 0.0
for idx,val in enumerate(input_list):
idx_val_sum += idx*val
val_sum += val
out = idx_val_sum/float(val_sum)
if out%1.0==0.0:
return out
else:
raise ValueError("Input list has non-integer centorid.")
这篇文章应该是回复小号2012年6月14日评论的评论,但我没有足够的声誉。"订单"在idx_val_sum
中隐式跟踪,这是按值加权的累积仓位总和。
编辑:
马特,谢谢你的观察。我以为这是一个伪代码问题,但现在我看到了C++标签。以下是一些(未经测试的)C++,并附有评论。
一个直观的例子是一个简单的杠杆臂问题:如果你有一个杠杆,在位置 x1 和 x2 处有两个力 f1 和 f2 作用于它,你可以通过在位置 (f1*x1+f2*x2)/(f1+f2) 施加力来防止系统旋转。连续系统需要对x和f的乘积进行积分,但是具有离散位置和力的杠杆是这个问题的一个很好的类比。
// untested code:
float centroid(float * vec, int vec_length){
float idx_val_sum = 0.0;
float val_sum = 0.0;
for (idx = 0; idx < vec_length; idx++){
// keep a running sum of the product of the index and the value
idx_val_sum += float(idx)*vec[idx];
// similarly, keep a running sum of the index
val_sum += vec[idx];
}
// return the quotient of the product-sum and the index sum:
return idx_val_sum/val_sum;
}
O(n) 且不需要更多空间的解决方案
def balance_array(arr):
if len(arr) < 3:
return False
for i in range(1, len(arr)+1):
lsum = sum(arr[:i])
rsum = sum(arr[(i+1):])
if lsum == rsum:
return True
return False
测试
test_arrays = [[5, 3, 7, 0, 9], [5,2,3,1,4,6], [1,0,1], [1,6,5,1,2,3,1], [1,1], [], [1], [1,2,9,4,-1], [5, 4, 7, 0, 9], [1, -1, 1, 0, 1, -1, 1]]
for i in test_arrays:
print(f'{i}t{balance_array(i)}')
[5, 3, 7, 0, 9] False
[5, 2, 3, 1, 4, 6] True
[1, 0, 1] True
[1, 6, 5, 1, 2, 3, 1] True
[1, 1] False
[] False
[1] False
[1, 2, 9, 4, -1] True
[5, 4, 7, 0, 9] True
[1, -1, 1, 0, 1, -1, 1] True
我相信你正在寻找质心,这里有一个用 Go 编写的解决方案:
func centerOfGravity(a []int) float64 {
tot := 0.0
mass := 0.0
for i := range a {
tot += float64(i) * float64(a[i])
mass += float64(a[i])
}
return tot / mass
}
这为您提供了数组中质心的索引,假设数组从 0 开始。 它可以返回非整数结果,因为质心可以在数组范围内的任何地方。
- 正在查找文档以获得PS4平台的C++中的设备信息
- 在C++中查找文件
- 模板元程序查找相似的连续类型名称
- 在UNIX系统中使用DIR查找文件的字节大小
- 查找最接近的大于当前数字的数字的索引
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 查找后更改类变量
- 使用正则表达式regex_search在字符串中查找字符串
- 使用gcc从静态链接的文件中查找可选符号
- 在C++中查找范围的长度
- 算法问题:查找从堆栈中弹出的所有序列
- 在Windows中查找扬声器输出的当前音量级别
- 如何在C++中使用X509证书模在令牌中查找私钥
- 使用.find函数在c++中查找字符和另一个字符之间的大小
- 在 Windows 上,是否可以让 dll 在不使用 PATH 环境变量的情况下在另一个文件夹中查找依赖项?
- 在 for 循环中查找问题时遇到困难
- 如何在文件中查找字节序列
- 试图创建一个多线程程序来查找0-100000000之间的总素数
- 查找数组中的不平衡点
- 在数组中查找平衡点