【快排思想,最大&和】
给定一个非负整数成的数组,长度一定大于1,想知道数组中那两个数&的结果最大,返回这个最大结果,时间复杂度为O(N), 额外空间复杂度为O(1)。
我的想法:对于每个数 1101110011,与他&最大的数,肯定是他自己,或这个比他大的数,那么完全可以将一个树,从大到小排个序,第i个数和第i+1个数的&,就是这个数与数组中所有数&的最大值,遍历一遍数组找到最大值。但是 比如10101,但是如果他相邻的数是010 00,00100,这样的话,就不成立了。
左神想法:按照位数进行比较,一个非负整数,也就是32位,我们只需要一次比较每一个数的0-30位,最高位为符号位。从第31位开始比较,寻找第31位有几个数字有1,假设为N,总共有三种可能性:
if N>2:在N>2的所有数字中继续比较下一位,也就是第30位为1的数字
if N=2:直接返回这两个数
if N<2:区间不变,继续比较下一位
时间复杂度 循环遍历31次,内每一次的时间复杂度为N所以时间复杂度为O(N)。
空间复杂读 采用快排的思想,在原数组中修改,分为有效区和垃圾区,规定(0,M-1)为有效区,(M+1, arr.length-1)为垃圾区,遍历arr[i] 如果当前位为1,i++,否则,sw ap(arr,i,M--),i不变。M的值就是有效区的长度。
public static int process(int[] arr) {
// 0-M是有用区,M+1 到最后是垃圾区,错了吧,注意下标和个数,M==2表示有用区的数量为2,
int M = arr.length;
int ans = 0;
for (int bit = 30; bit >= 0; bit--) {
int temp = M;
int i = 0;
// 遍历数组,并更新有用区和垃圾区
while (i < M) {
if ((arr[i] & (1 << bit)) == 0) {
swap(arr, i, M--);
} else {
i++;
}
}
if (M == 2) {
return arr[0] & arr[1];
}
// 如果是<2的话,那么恢复更新前的有用区,继续比较下一位
if (M < 2) {
M = temp;
}
// 有用区长度,大于二,说明ans这一位一定是1,更新ans,继续比较下一位
if (M > 2) {
ans |= (1 << bit);
}
}
return ans;
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[j] = arr[i];
arr[i] = arr[j];
}
【二叉树的递归套路,相机最小覆盖问题】
给定一个二叉树,我们在树的节点上安装摄像头。 节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。 计算监控树的所有节点所需的最小摄像头数量。 1.给定树的节点数的范围是
[1, 1000]。
2.每个节点的值都是0。
leetcode.cn/problems/bi…
我的想法:
1.每一个二叉树节点都有两种选择,放相机和不放相机,如果按照这样暴力去解决的话,结局也会有两个中,相机没有被覆盖和相机被覆盖了,然后从覆盖的可能中找到最小数量,时间复杂度为2^N。
2. 能否剪枝呢?当前节点放了相机,那么它的父节点和子节点都会覆盖到,所以完全可以不用放,当前节点没放相机,并且还没有被覆盖,那么他的下一个节点必须要放,因为这样才能保证全覆盖。所以定义f(n)表以N为头的节点监控所有节点需要的最小摄像头数量,尽可能的避免交叉覆盖。两种可能性:
if n不放摄像头,那么他的子节点必须要放有一个要放。
if n放摄像头, 那么他的子节点都不要放摄像头。
左神想法:
二叉树递归套路,整理可能性,父节点要给子节点要什么样的信息,三种可能性,三种可能性的前提都是x节点的所有子树都被覆盖的情况下:
1.x节点被覆盖,x节点上有相机,以x为头的树至少需要几个相机
2.x节点被覆盖,x节点上没有相机,以x为头的树至少需要几个相机
3.x节点没被覆盖,以x为头的树至少需要几个相机
但是为什么这么分可能性?。按照我的想法,分为x位置放相机和不放相机子树全部被覆盖的相机数量划分可能性,两种可能性来分,看看我们需要向子节点要什么样的信息。当x节点放相机的情况下,我们需要向x.left要x.left节点不放相机,x不被覆盖但是x.left的子树全被覆盖的情况,右节点也是同理。x不放相机,我们就要要求子节点至少有一个节点放相机,并且子节点还要全部被覆盖,所以就是 x.left放相机,并且x及其子树都要被覆盖。或者x.left不放相机,但是x及其子树都要被覆盖的情况。这样分的情况下,我们需要向子节点要这三类信息,可以按照放不放相机,x节点及其字节点都被覆盖的最小数量这样分两种可能性不够的,需要分三种情况,要考虑每种情况的所有可能性,不要用贪心,找到所有可能性之后,取最小值即可。
X节点不被覆盖 左孩子没有摄像头被覆盖+右孩子没有摄像头被覆盖。
X节点没有摄像头被覆盖 左孩子有摄像头被覆盖+右孩子有摄像头被覆盖 或者 左孩子有摄像头被覆盖+右孩子无摄像头被覆盖 或者 左孩子无摄像头被覆盖+右孩子有摄像头被覆盖。
X节点有摄像头被覆盖 左孩子三种情况都可以取 无摄像头,有摄像头被覆盖,无摄像头被覆盖,三种情况取最小,同理右孩子也是三种情况取最小,最后再加1。
public class code27_监控二叉树 {
public static void main(String[] args) {
}
public int minCameraCover(Node root) {
Info info = process(root);
return (int) Math.min(info.uncovered + 1, Math.min(info.coveredHasCamera, info.coveredNoCamera));
}
public static Info process(Node head) {
if (head == null) {
// 如果是空数,肯定是全覆盖,并且没有摄像头,所以uncovered=最大值,coveredHasCamera=最大值,coveredNoCamera=0;
return new Info(Integer.MAX_VALUE, 0, Integer.MAX_VALUE);
}
Info left = process(head.left);
Info right = process(head.right);
// 当前节点不覆盖,没有摄像头,我这个节点不覆盖,我的两个子节点一定是没有摄像头并且被覆盖,不然我就覆盖了
long uncovered = left.coveredNoCamera + right.coveredNoCamera;
// 覆盖没有摄像头,我的子节点一个有摄像头一个没有摄像头,并且被覆盖,还包括,子节点都包含摄像头,并且covered
long coveredNoCamera = Math.min(right.coveredHasCamera+ left.coveredHasCamera,Math.min(left.coveredHasCamera + right.coveredNoCamera, left.coveredNoCamera + right.coveredHasCamera));
// 覆盖有摄像头,我的子节点都可以不覆盖没有摄像头,也可以覆盖没有摄像头,也可以覆盖有摄像头,左边三种情况取最小值,右边是那种情况取最小值
long coveredHasCamera = Math.min(left.uncovered, Math.min(left.coveredHasCamera, left.coveredNoCamera)) + Math.min(right.uncovered, Math.min(right.coveredNoCamera, right.coveredHasCamera)) + 1;
return new Info(uncovered, coveredNoCamera, coveredHasCamera);
}
private static class Info {
long uncovered;
long coveredNoCamera;
long coveredHasCamera;
public Info(long uncovered, long coveredNoCamera, long coveredHasCamera) {
this.uncovered = uncovered;
this.coveredNoCamera = coveredNoCamera;
this.coveredHasCamera = coveredHasCamera;
}
}
private static class Node {
Node left = null;
Node right = null;
public Node(Node left, Node right) {
this.left = left;
this.right = right;
}
public Node() {
}
}
}