题目描述:
给定正整数 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;
}