每日一题——将数组分为和相等的三个部分【3.11】

每日一题——将数组分为和相等的三个部分【3.11】

  • 题目
  • 基本思路
  • 具体代码

题目

  leetcode题目:(链接https://leetcode-cn.com/problems/partition-array-into-three-parts-with-equal-sum)。
  给你一个整数数组 A,只有可以将其划分为三个和相等的非空部分时才返回 true,否则返回 false。
  形式上,如果可以找出索引 i+1 < j 且满足 (A[0] + A[1] + … + A[i] == A[i+1] + A[i+2] + … + A[j-1] == A[j] + A[j-1] + … + A[A.length - 1]) 就可以将数组三等分。

示例 1:

输入:[0,2,1,-6,6,-7,9,1,2,0,1]
输出:true
解释:0 + 2 + 1 = -6 + 6 - 7 + 9 + 1 = 2 + 0 + 1

示例 2:

输入:[0,2,1,-6,6,7,9,-1,2,0,1]
输出:false

示例 3:

输入:[3,3,6,5,-2,2,5,1,-9,4]
输出:true
解释:3 + 3 = 6 = 5 - 2 + 2 + 5 + 1 - 9 + 4

基本思路

  最直接的方法为暴力法,从前往后拼凑第一段的和,然后看有没有相等的第二段和第三段;
  例如输入为[0,2,1,-6,6,-7,9,1,2,0,1],我们先假设第一段为0,找有没有和0的第二段和第三段。没有的话再假设第一段为 0+2=2,找有没有和为2的第二段和第三段,依此下去直到遍历完或者找到符合条件的为止。这种方法费时费力,有大量的冗余计算,并且编码过程也很绕。

  后面看到评论里有说用双指针,豁然开朗,我们完全可以设两个指针分别指向数组的头和尾。因为能被分为三段的前提条件是数组和能被3整除,所以我们只需要找到三段,每段和为sum / 3即可。从前往后找一段,从后往前找一段,再确认中间一段存在。
  但是我一开始使用这种方法还是想得太多了,前后只需找到一个符合条件的就可以了,按照贪心算法,头指针取最小,尾指针取最大,中间的一定满足。

具体代码

暴力法:

//Java
class Solution {
    public boolean canThreePartsEqualSum(int[] A) {
		int length = A.length;
		int sum = 0;
		for(int i = 0; i < length - 2; i++) {
			sum = sum + A[i];       //求第一段和
			int temp1 = 0;
			for(int j = i + 1; j < length - 1; j++) {
				temp1 = temp1 + A[j];       //求第二段和
				if(temp1 == sum) {
					int temp2 = 0;
					for(int k = j + 1; k < length; k++) {
						temp2 = temp2 + A[k];       //求第三段和
						if(temp2 == sum && k == length - 1) {
							return true;
						}
					}
				}
			}
		}
		return false;
    }
}

双指针最初代码:

//Java
class Solution {
	public boolean canThreePartsEqualSum(int[] A) {
		int sum = 0;
		for(int i = 0; i < A.length; i++) {      //只有数组和能被三整除才有可能三等分
			sum = sum + A[i];
		}
		if(sum % 3 != 0) {
			return false;
		}
		int avar = sum / 3;
		
		int low = 0;
		int high = A.length - 1;
		
		int first = 0;       //第一段
		int second = 0;      //第二段
		int third = 0;       //第三段
		boolean undergo = false;        //确认第二段的存在
		
		while(high > low) {
			first = first + A[low];  //从前往后第一段求和
			if(first == avar) {      //第一段符合标准时,从后往前计算第三段
				while(high > low) {
					third = third + A[high];
					if(third == avar) {     //第三段符合标准时,计算第二段
						for(int i = low + 1; i < high; i++) {
							undergo = true;      //确认第二段存在
							second = second + A[i];
						}
						if(second == avar && undergo) {
							return true;
						} else {          //得不到正确的第二段时,归零
							second = 0;
						}
					}
					high--;
				}
			}
			high = A.length - 1;       //将尾指针和第三段置回原位
			third = 0;
			low++;         //增大第一段,重新计算
		}
		return false;
    }
}

双指针优化后代码

//Java
class Solution {
	public boolean canThreePartsEqualSum(int[] A) {
		int sum = 0;
		for(int i = 0; i < A.length; i++) {      //只有数组和能被三整除才有可能三等分
			sum = sum + A[i];
		}
		if(sum % 3 != 0) {
			return false;
		}
		int avar = sum / 3;
		
		int low = 0;
		int high = A.length - 1;
		
		int first = 0;          //第一段
		int third = 0;          //第三段
		
		first = A[low];         //初始化,为了避免默认值0正好等于avar的情况
		third = A[high];
		while(low + 1 < high) {
			//这个if一定要放在while的最前面,这是为了保证满足low+1 < high。
			//如果不放在第一个的话,low和high可能会变化,无法保证第二段存在。
			if(first == avar && third == avar) {
				return true;
			}
			
			if(first != avar) {
				low++;
				first = first + A[low];	
			}
			if(third != avar) {
				high--;
				third = third + A[high];
			}
		}
		return false;
    }
}

时间复杂度:仅遍历了一遍数组,时间复杂度为 O ( N ) O(N) O(N)
空间复杂度:仅使用常数个变量,空间复杂度为 O ( 1 ) O(1) O(1)

你可能感兴趣的:(算法)