连续子段绝对值最小问题

【座右铭】1. 想要成为行家,就必须尝试解决大量的问题;

                    2. 解决大量问题并不代表能解决所有问题,而是表示解决下一个问题的几率变大了

1. 现有一个数组,里面包含了正数和负数,取其中若干个连续的数,要求这些数的和的绝对值最小【问题来源于论坛】

第一部分:思路

1. 对数组A[1...N],做和运算S[1...N],其中S[1] = A[1]; S[2] = A[1]+A[2];...;S[N]=A[1]+A[2]+A[3]+...+A[N]

2. 对S[1...N]从小到大排序

3. 连续子段绝对值最小:Min{ |S[i]|,S[i+1]-S[i]} 1<=i<=N

第二部分:Java代码,不考虑异常情况

//待修正,在获取连续子段在原数组中的开始索引和结束索引有bug。

//代码中存在错误的判断条件:S[i+1]对应的索引大于S[i]对应的索引

//问题提出日期:4.18

/**
	 * 现有一个数组,里面包含了正数和负数,取其中若干个连续的数,要求这些数的和的绝对值最小
	 * @param source
	 * 数组
	 * @param len
	 * 数组长度
	 * @return
	 * 一个三元组,分别是最小值,连续子段在原数组中的开始索引,结束索引
	 */
	public static int[] minAbs(int[] source, int len)
	{
		//保存最小值,连续子段在原数组中的开始索引,结束索引
		int[] result = new int[3];
		//和数组
		int[] sum = new int[len];
		sum[0] = source[0];
		for(int i=1;i<len;i++)
		{
			source[i] += source[i-1];
			sum[i] = source[i];
		}
		//排序
		quicksort(sum, 0, len-1);
		
		boolean isFromZero = false;
		result[0] = sum[0]>0?sum[0]:-1*sum[0];
		isFromZero = true;
		result[1] = 0;
		result[2] = sum[0];
		
		for(int j=1;j<len;j++)
		{
			int cur = sum[j]>0?sum[j]:-1*sum[j];
			if(cur<result[0])
			{
				result[0] = cur;
				isFromZero = true;
				result[1] = 0;
				result[2] = sum[j];
			}
			
			cur = sum[j] - sum[j-1];
			if(cur<result[0])
			{
				result[0] = cur;
				isFromZero = false;
				result[1] = sum[j-1];
				result[2] = sum[j];
			}
		}
		//还原数组
		int k = len - 1;
		for(;k>=0;k--)
		{
			if(source[k]==result[2])
			{
				result[2] = k;
				break;
			}
		}
		if(!isFromZero)
		{
			for(k=0;k<result[2];k++)
			{
				if(source[k]==result[1])
				{
					result[1] = k+1;
					break;
				}
			}
		}
		
		for(k=len-1;k>0;k--)
		{
			source[k] -= source[k-1];
		}
		return result;
	}
	/**
	 * 快速排序
	 */
	static void quicksort(int[] source, int left, int right)
	{
		if(left<right)
		{
			int mid = partition(source, left, right);
			quicksort(source, left, mid-1);
			quicksort(source, mid+1, right);
		}
	}
	static int partition(int[] source, int left, int right)
	{
		int start = left, end = right;
		int tmp = 0;
		left++;
		while(left<=right)
		{
			while(left<=end&&source[left]<=source[start])
			{
				left++;
			}
			while(source[right]>source[start])
			{
				right--;
			}
			if(left<right)
			{
				tmp = source[left];
				source[left] = source[right];
				source[right] = tmp;
			}
		}
		tmp = source[start];
		source[start] = source[right];
		source[right] = tmp;
		return right;
	}

第三部分:测试用例

数组{2,2,2,2}:返回{2,0,0}

数组{2,-2,1,3,4,-4}:返回{0,0,1}

数组{-2,1,2,-1,4}:返回{0,0,3}

数组{-2,3,1,-3,4,6,-7,2,8}:返回{0,3,6}

数组{-2,-6,-7,-3}:返回{2,0,0}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值