2021-10-28重新排序得到2的幂

题目描述:

给定正整数 N ,我们按任何顺序(包括原始顺序)将数字重新排序,注意其前导数字不能为零。

如果我们可以通过上述方式得到 2 的幂,返回 true;否则,返回 false。

其中: 1<= N <= 1e9。

解法:

1 判断数子是2的幂

在计算机中的整型数据就是使用二进制存放的,因此在计算机中判断整数是否是2的幂是一个很方便的事情。2的幂用二进制表示具有鲜明的特点,例如 8 = 2^3 使用二进制表示为

(00000000 00000000 00000000 00001000)

以上使用4字节的int表示的,可以看到32位中有且仅有一位位1,其他全为0。

利用这个特点,可以将判断整数为2的幂的函数写成:

bool check(int n){
	return 0 == (n & (n-1));
}

可以看到确实是非常简单。

2 遍历满足条件的所有组合,并尽可能避免遍历数值上重复的组合情况

在使用回溯遍历所有的组合前,先要对数据进行处理,将数字的每一位分开存放,并且使用排序算法从低到高(或者从高到低)排序。

整个问题变成往一个个“空格”中填入一位位数字。

使用递归函数完成每层的遍历,形参中 arr 存储上面整理好的数据, mrk (mark)标记当前那些数字已经被选入组合了,len 表示 arr 和 mrk 的长度,pivot 表示当前对第 pivot 个空格选择一个数字放入, ret (result) 表示当前各个空格填入的情况。

题目要求在组合的开始不能是0,所以在回溯到 pivot==0 时检测到 arr[i] == 0 就跳过。在 pivot!=0时是不用检测的,因为第一个空格不可能为0。

为了避免在同一层级(pivot相同,ret前部分相同)中对相同数值的数字的重复填入,使用flg标记数组辅助检测重复。比如arr为[1 1 2] 在第一个空格填入1只需要填入一次,在for循环中遍历到第二个1的时候往前看和1相同值的数,如果有一个与它相同值的数(第一个1)的flg对应位1,表示此层与我(第二个1)相同的数已经填过一次了,就把我略掉。

每一层的递归算法如下:

bool go(char * arr, char * mrk, int len, int pivot, char * ret){
	if(pivot == len)
		return check(combine(ret, len));
	int i, j;
	char flg[N];
	int flag = 0;;
	for(i=0; i<len; i++)
		flg[i] = 0;// 标识在这一层中 i 位置是否试过了
	for(i=0; i<len; i++){
		if(mrk[i] == 0 && flg[i] == 0){
			if(pivot == 0 && arr[i] == 0)
				continue;// 第一个空格不能是 0
			flag = 0;// reset
			for(j=i-1; j>=0; j--){
				if(arr[j] == arr[i] && flg[j] == 1){
					flag = 1;// 与 arr[i] 相同的数已经在这个位置试过了
					break;
				}
			}
			if(flag == 1)
				continue;
			mrk[i] = 1;
			ret[pivot] = arr[i];
			//printf("ret[%d]=arr[%d]=%d\n", i, i, arr[i]);
			if(go(arr, mrk, len, pivot+1, ret))
				return 1;
			mrk[i] = 0;
			flg[i] = 1;
		}
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值