算法和数据结构学习之旅第一篇

144 阅读3分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。

一、题目一

给定一个非负数组成的数组,长度一定大于1,想知道数组中哪两个数&的结果最大,返回这个最大结果。

要求:时间复杂度O(N),额外空间复杂度O(1)

提示:

  • 非负数即数组中都是正整数,任何数都可以用二进制表示,非负数说明最高位为0
  • 剩余31位,从31位到0位,两个数每位上进行&操作,尽量高位&上结果为1,才能最大
  • 数据始终在数组上移动,没有产生额外的空间

思路:

  • 二进制&,不是1就是0,所以最高位&为1时,结果最大。
  • 始终在数组中通过数据交换淘汰数据:空间复杂度O(1)
  • M:指向数据淘汰的指针
  • arr[0...M-1]:有效数
  • arr[M...]:淘汰的数,垃圾数
    // O(N^2)的暴力解
    public static int maxAndValue1(int[] arr) {
        int N = arr.length;
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < N; i++) {
            for (int j = i + 1; j < N; j++) {
                max = Math.max(max, arr[i] & arr[j]);
            }
        }
        return max;
    }

    // 时间复杂度O(N),额外空间复杂度O(1)
    public static int maxAndValue2(int[] arr) {
        // arr[0...M-1]  arr[M....]
        int M = arr.length;
        int ans = 0;
        for (int bit = 30; bit >= 0; bit--) {
            // arr[0...M-1] arr[M...]
            int i = 0;
            int tmp = M;
            while (i < M) { // arr[0...M-1]
                if ((arr[i] & (1 << bit)) == 0) {
                    swap(arr, i, --M);
                } else {
                    i++;
                }
            }
            if (M == 2) { // arr[0,1]
                return arr[0] & arr[1];
            }
            if (M < 2) { // 不淘汰,接受该位置为0的情况
                M = tmp;
            } else { // > 2个数  bit位上有1
                ans |= (1 << bit);
            }
        }
        return ans;
    }

    public static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

二、题目二

给定一个二叉树,我们在树的节点上安装摄像头。

节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。

计算监控树的所有节点所需的最小摄像头数量。

相机最小覆盖问题

思路:

x为空树,需不需要被覆盖?不需要覆盖,可以认为空树是直接被覆盖的的状态,
所以空树就不存在被覆盖的情况,假设为系统最大。
空树上边没法放相机,假设为系统最大。

空树就认为被覆盖,且不能放相机

以任意节点为头节点的二叉树,存在三种可能:
1. 头节点放向机,且被覆盖
2. 头节点没放相机,且被覆盖
3. 头节点没放相机,且没被覆盖

image.png

    public static class TreeNode {
        public int value;
        public TreeNode left;
        public TreeNode right;
    }

    public static int minCameraCover(TreeNode root) {
        Info data = process(root);
        return (int) Math.min(data.uncovered + 1, Math.min(data.coveredNoCamera, data.coveredHasCamera));
    }

    // 潜台词:x是头节点,x下方的点都被覆盖的情况下
    public static class Info {
        public long uncovered; // x没有被覆盖,x为头的树至少需要几个相机
        public long coveredNoCamera; // x被相机覆盖,但是x没相机,x为头的树至少需要几个相机
        public long coveredHasCamera; // x被相机覆盖了,并且x上放了相机,x为头的树至少需要几个相机

        public Info(long un, long no, long has) {
            uncovered = un;
            coveredNoCamera = no;
            coveredHasCamera = has;
        }
    }

    // 所有可能性都穷尽了
    public static Info process(TreeNode X) {
        if (X == null) { // base case
            return new Info(Integer.MAX_VALUE, 0, Integer.MAX_VALUE);
        }

        Info left = process1(X.left);
        Info right = process1(X.right);
        // x uncovered x自己不被覆盖,x下方所有节点,都被覆盖
        // 左孩子: 左孩子没被覆盖,左孩子以下的点都被覆盖
        // 左孩子被覆盖但没相机,左孩子以下的点都被覆盖
        // 右孩子被覆盖也有相机,右孩子以下的点都被覆盖
        long uncovered = left.coveredNoCamera + right.coveredNoCamera;

        // x下方的点都被covered,x也被cover,但x上没相机
        long coveredNoCamera = Math.min(
                // 1)
                left.coveredHasCamera + right.coveredHasCamera,

                Math.min(
                        // 2)
                        left.coveredHasCamera + right.coveredNoCamera,

                        // 3)
                        left.coveredNoCamera + right.coveredHasCamera));


        // x下方的点都被covered,x也被cover,且x上有相机
        long coveredHasCamera =
                Math.min(left.uncovered, Math.min(left.coveredNoCamera, left.coveredHasCamera))

                        + Math.min(right.uncovered, Math.min(right.coveredNoCamera, right.coveredHasCamera))

                        + 1;

        return new Info(uncovered, coveredNoCamera, coveredHasCamera);
    }