原题描述
给你一个整数数组 nums ,返回其中 按位与三元组 的数目。
按位与三元组 是由下标 (i, j, k) 组成的三元组,并满足下述全部条件:
0 <= i < nums.length
0 <= j < nums.length
0 <= k < nums.length
nums[i] & nums[j] & nums[k] == 0 ,其中 & 表示按位与运算符。
1 <= nums.length <= 1000
0 <= nums[i] < 2^16
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/triples-with-bitwise-and-equal-to-zero
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
链接
解题过程
- 首先想到用三层for循环。
int countTriplets(int *nums, int numsSize){
int result = 0;
for (int i = 0; i < numsSize; i++){
for (int j = 0; j < numsSize; j++){
for (int k = 0; k < numsSize; k++){
if((nums[i] & nums[j] & nums[k]) == 0){
result++;
}
}
}
}
return result;
}
时间复杂度为 O(n^3)。提交后时间超限。
- 官方题解
(大概意思差不多,官方是声明的动态数组。)
int countTriplets(int* nums, int numsSize) {
int tmp_num[1 << 16];
memset(tmp_num,0,sizeof(tmp_num));
for (int i = 0; i < numsSize; i++) {
int x = nums[i];
for (int j = 0; j < numsSize; j++) {
int y = nums[j];
tmp_num[x & y]++;
}
}
int result = 0;
for (int i = 0; i < numsSize; i++) {
int x = nums[i];
for (int j = 0; j < (1 << 16); ++j) {
if ((x & j) == 0) {
result += tmp_num[j];
}
}
}
return result;
}
官方讲解的很好,用空间换时间,将前两遍的循环结果存储下来记为一个新矩阵。再将其进行下一个阶段的循环(第三给位置),将时间复杂度降了下来。
- 官方示例代码
//前面是一样的空间换时间
//后一个循环定位 x 时,直接采用异或操作定位到最大的和x相与为0的元素,之后逐次遍历更小的可能相与为0的元素。(妙)
for (int i=0; i<numsSize; i++){
int x = nums[i] ^ 0xffff;
for (int sub = x; sub; sub = (sub - 1) & x) {
result += tmp_num[sub];
}
result += tmp_num[0];
}
分享一下自己对这个x和sub的理解:
博主64位机器,x是int类型,32位,且0 <= nums[i] < 2^16,所以nums[i]前16位均是0。
- x = nums[i] ^ 0xffff:此过程相当于将nums[i]的低16位01相反(即0变为1,1变为0),此时便会得到最大的与nums[i]相交为0的数x。
- 接下来寻找小于x但大于0的数,此时示例代码没有用判断语句来寻找和nums[i]相与为0的数,(写成代码大概是下面这样
for (int sub = x; sub; sub --) {
if((nums[i] & sub) == 0) {
result += tmp_num[sub];
}
}
)。
而是sub = (sub - 1) & x。
首先x是最大的(因为x的2进制表示中能为1的都为1了)。
其次:要找小于x的sub元素,只需要将x中的1依次变为0即可。
例如:假设nums[i] = 1111 1111 1100 1000
则 x = 0000 0000 0011 0111
则sub依次为x,0000 0000 0011 0110
0000 0000 0011 0101
0000 0000 0011 0100
0000 0000 0011 0011
…
0000 0000 0011 0000
接下来为0000 0000 0010 0110,这步就是&x的好处,跳过了多次循环。
…
将其用数学语言表示即为sub = (sub - 1) & x 。其中&x保证了 sub&nums[i]=0;sub-1负责将sub逐渐减小;由此便会得到所有和nums[i]相与为0的sub元素。
作者才疏学浅,文章多有不足,请大家多多指教。