473. 火柴拼正方形——回溯算法

473. 火柴拼正方形(Java)

题目链接:https://leetcode-cn.com/problems/matchsticks-to-square/
难度:中等

1.题目

输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。

2.示例

输入: [1,1,2,2,2]
输出: true
解释: 能拼成一个边长为2的正方形,每边两根火柴。

3.题解

思路:先判断所有火柴的总长度是否为4的倍数。如果不是则直接返回false。如果是4的倍数就尝试将火柴放入四个边看能否组成正方形。

class Solution {
    public boolean makesquare(int[] matchsticks) {
		int sum = 0;
		for(int matchstick: matchsticks) sum += matchstick;
		if(sum==0||(sum & 3)!=0) return false;
		return backtrack(matchsticks,0,sum >>2,new int[4]);
    }
    //index表示当前对应的arr[index],target则是正方体的边长,size数组存储各边的长度
	//该函数是对火柴数组arr中的第index根进行相应的操作
	public boolean backtrack(int[] arr,int index,int target,int[] size){
		//当数组arr的最后一根火柴已经全部加入size数组中
		if(index == arr.length){
			//判断size中的边长是否相等
			if(size[0]==size[1]&&size[1]==size[2]&&size[2]==size[3]) return true;
			//不相等则表示没有对应的解
			return false;
		}
		//当数组arr还有火柴没有用完时
		for(int i=0;i<4;i++){
			//如果size[i]的长度加上arr[index]超过了target,则应该结束此次循环
			if(size[i]+arr[index]>target) continue;
			//如果相加和不超过target,就可以进行下一步
			//i边加上相对应的长度,然后对下一个火柴长度进行判断
			size[i] += arr[index];
			if(backtrack(arr,index+1,target,size)) return true;
			//如果在接下来的火柴长度判断中不满足题目要求,则将此时的火柴长度减去,对下一个边长进行判断
			size[i] -= arr[index];
		}
		return false;
	}
}

复杂度分析
时间复杂度:O(4N),N是火柴的数量
空间复杂度:O(N)。

代码优化

如果数组的前面元素数值较小,则可能导致递归的深度较高。于是我们可以先对火柴数组进行排序,从大的火柴进行填入判断。

class Solution {
    public boolean makesquare(int[] matchsticks) {
		int sum = 0;
		for(int matchstick: matchsticks) sum += matchstick;
		if(sum==0||(sum & 3)!=0) return false;
		Arrays.sort(matchsticks);
		return backtrack(matchsticks,matchsticks.length-1,sum >>2,new int[4]);
    }
    //index表示当前对应的arr[index],target则是正方体的边长,size数组存储各边的长度
	//该函数是对火柴数组arr中的第index根进行相应的操作
	public boolean backtrack(int[] arr,int index,int target,int[] size){
		//当数组arr的最后一根火柴已经全部加入size数组中
		if(index == -1){
			//判断size中的边长是否相等
			if(size[0]==size[1]&&size[1]==size[2]&&size[2]==size[3]) return true;
			//不相等则表示没有对应的解
			return false;
		}
		//当数组arr还有火柴没有用完时
		for(int i=0;i<4;i++){
			//如果size[i]的长度加上arr[index]超过了target,则应该结束此次循环
			//如果此时的size[i]跟前面的size[i-1]相等,
			//就说明在上一个size[i-1]的循环中已经证明这个arr[index]不适合目前的size[i]
			//则对于此时的index也不存在解
			if(size[i]+arr[index]>target||(i>0&&size[i]==size[i-1])) continue;
			//如果相加和不超过target,就可以进行下一步
			//i边加上相对应的长度,然后对下一个火柴长度进行判断
			size[i] += arr[index];
			if(backtrack(arr,index-1,target,size)) return true;
			//如果在接下来的火柴长度判断中不满足题目要求,则将此时的火柴长度减去,对下一个边长进行判断
			size[i] -= arr[index];
		}
		return false;
	}
}

4.总结

回溯算法的经典模板

private void backtrack("原始参数") {
    //终止条件(递归必须要有终止条件)
    if ("终止条件") {
        //一些逻辑操作(可有可无,视情况而定)
        return;
    }
    for (int i = "for循环开始的参数"; i < "for循环结束的参数"; i++) {
        //一些逻辑操作(可有可无,视情况而定)

        //做出选择

        //递归
        backtrack("新的参数");
        //一些逻辑操作(可有可无,视情况而定)

        //撤销选择
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值