左神高级提升班1 很重要的题目

【案例1】

【题目描述 难度非常高】

 【思路解析】

因为要求额外空间复杂度为O(1),所以我们只能使用有限几个变量,来得到整个数组所在的城市距离首都的距离。因为数组paths[i]表示,i城市指向paths[i]城市,我们可以利用这个指向关系(next为下次要去的城市,last为当前城市。并且在往前遍历时,要记录回去的信息,即paths[next的值]=last的值,这样我们就可以再通过last和next往回走),一直往前遍历,直到next==paths[next],或者paths[next] < 0,我们便开始往回走,并且回走时,如果城市i距离首都为x,则给paths[i]赋值为-x。

然后得到了这个距离数组后,再通过判断距离数组的正负来统计距离(通过指向关系来进行循环计数)的个数。

【代码实现】

import java.util.Arrays;

/**
 * @ProjectName: study3
 * @FileName: Ex1
 * @author:HWJ
 * @Data: 2023/9/14 22:30
 */
public class Ex1 {
    public static void main(String[] args) {
        int[] paths = {9, 1, 4, 9, 0, 4, 8, 9, 0, 1};
        pathToDistance(paths);
        distanceToCount(paths);
        System.out.println(Arrays.toString(paths));
    }

    public static void pathToDistance(int[] paths) {
        int n = paths.length;
        int capital = -1;
        for (int start = 0; start < n; start++) {
            if (paths[start] == start) {
                capital = start;
            } else if (paths[start] > -1) {
                int last = start;
                int next = paths[last];
                while (paths[next] != next && paths[next] > -1) {
                        int num = paths[next];
                        paths[next] = last;
                        last = next;
                        next = num;
                }
                int i = paths[next] == next ? 0 : paths[next];
                while (last != start) {
                    int num = paths[last];
                    paths[last] = --i;
                    last = num;
                }
                paths[last] = --i;
            }
        }
        paths[capital] = 0;
    }

    public static void distanceToCount(int[] paths){
        for (int i = 0; i < paths.length; i++) {
            int cur = paths[i];
            paths[i] = Math.max(paths[i], 0);
            while (cur <= 0){
                int next = paths[-cur];
                if (next < 0){
                    paths[-cur] = 1;
                    cur = next;
                }else {
                    paths[-cur]++;
                    break;
                }
            }

        }
    }
}

【案例2】

【题目描述 分糖果问题】

一群孩子做游戏,现在请你根据游戏得分来发糖果,要求如下:

  1. 每个孩子不管得分多少,起码分到一个糖果。

  2. 任意两个相邻的孩子之间,得分较多的孩子必须拿多一些糖果。

  3. 【进价】要求相邻的两个孩子如果得分相同,得到的糖果应该相同。

  4. 要求时间复杂度为O(N)

【思路解析】

我们可以通过得分数组得到一个得分的折线图(即升降情况),然后通过从左向右遍历这个升降情况得到每个人应该分到的糖果数。然后通过从右向左遍历这个升降情况得到每个人应该分到的糖果数。然后对应求最大即为每个孩子的糖果数。遍历时要求升序糖果数++,如果降序或者保持不变,使糖果数变回1.

【进价思路分析】

我们可以通过得分数组得到一个得分的折线图(即升降情况),然后通过从左向右遍历这个升降情况得到每个人应该分到的糖果数。然后通过从右向左遍历这个升降情况得到每个人应该分到的糖果数。然后对应求最大即为每个孩子的糖果数。遍历时要求升序糖果数++,如果降序使糖果数变回1,如果保持不变,则使糖果数保持不变。

【代码实现】

/**
 * @ProjectName: study3
 * @FileName: Ex2
 * @author:HWJ
 * @Data: 2023/9/15 0:07
 */
public class Ex2 {
    public static void main(String[] args) {
        int[] scores = {1, 2, 2};
        System.out.println(candy1(scores));
        System.out.println(candy2(scores));
    }

    public static int candy1(int[] scores){
        int[] left = new int[scores.length];
        int[] right = new int[scores.length];
        left[0] = 1;
        for (int i = 1; i < scores.length; i++) {
            if(scores[i] > scores[i - 1]){
                left[i] = left[i - 1] + 1;
            }else {
                left[i] = 1;
            }
        }
        right[scores.length - 1] = 1;
        for (int i = scores.length - 2; i >= 1; i--) {
            if(scores[i] > scores[i + 1]){
                right[i] = right[i + 1] + 1;
            }else {
                right[i] = 1;
            }
        }
        int res = 0;
        for (int i = 0; i < scores.length; i++) {
            res += Math.max(left[i], right[i]);
        }
        return res;
    }

    public static int candy2(int[] scores){
        int[] left = new int[scores.length];
        int[] right = new int[scores.length];
        left[0] = 1;
        for (int i = 1; i < scores.length; i++) {
            if(scores[i] > scores[i - 1]){
                left[i] = left[i - 1] + 1;
            }else if (scores[i] < scores[i - 1]){
                left[i] = 1;
            }else {
                left[i] = left[i - 1];
            }
        }
        right[scores.length - 1] = 1;
        for (int i = scores.length - 2; i >= 1; i--) {
            if(scores[i] > scores[i + 1]){
                right[i] = right[i + 1] + 1;
            }else if (scores[i] < scores[i + 1]){
                right[i] = 1;
            }else {
                right[i] = right[i + 1];
            }
        }
        int res = 0;
        for (int i = 0; i < scores.length; i++) {
            res += Math.max(left[i], right[i]);
        }
        return res;
    }
}

【超级进阶   非常难】

要求时间复杂度为O(N), 额外空间复杂度为O(1)。   实现特别困难。

【案例3】

【题目描述】

【思路解析】二叉树递归套路类题目

第一种思路,列出所有可能性,然后在递归时得到所有可能性,然后不断汇总求最优解。

能获得最优解的可能性如下:

(1)x结点以下都被照亮(包含x结点),且x上不放灯。

(2)x结点以下都被照亮(包含x结点),且x上放灯。

(3)x结点以下都被照亮(不包含x结点),但x上放灯。(因为这种情况可能引出下一个最优解,在x的父上放灯,影响的结点更多)。

第二种思路,我们在传递的时候已知最优解需要的是什么信息,利用贪心加速常数时间。

比如当左孩子和右孩子,未被覆盖时,x上必须放灯,如果左孩子和右孩子,被覆盖且放灯,则x上一定不放灯。如果左孩子和右子,被覆盖但没放灯,则x未被覆盖。

【代码实现】 

/**
 * @ProjectName: study3
 * @FileName: Ex3
 * @author:HWJ
 * @Data: 2023/9/15 0:16
 */
public class Ex3 {
    public static void main(String[] args) {

    }

    public static class Node{
        Node left;
        Node right;
    }

    public static class ReturnData{
        public int unCovered;
        public int coveredNoCamera;
        public int coveredHasCamera;

        public ReturnData(int unCovered, int coveredNoCamera, int coveredHasCamera) {
            this.unCovered = unCovered;
            this.coveredNoCamera = coveredNoCamera;
            this.coveredHasCamera = coveredHasCamera;
        }
    }

    public static int getMin(Node head){
        ReturnData data = process(head);
        return Math.min(data.unCovered, Math.min(data.coveredHasCamera, data.coveredNoCamera));
    }

    public static ReturnData process(Node node){
        if (node == null){
            return new ReturnData(Integer.MAX_VALUE, 0, Integer.MAX_VALUE);
        }
        ReturnData left = process(node.left);
        ReturnData right = process(node.right);
        int unCovered = left.coveredNoCamera + right.coveredNoCamera;
        int coveredNoCamera = Math.min(left.coveredHasCamera + right.coveredHasCamera,
                Math.min(left.coveredHasCamera + right.coveredNoCamera,
                        left.coveredNoCamera + right.coveredHasCamera));
        int coveredHasCamera = Math.min(left.unCovered,
                Math.min(left.coveredHasCamera, left.coveredNoCamera)) +
                Math.min(right.unCovered,
                        Math.min(right.coveredHasCamera, right.coveredNoCamera)) + 1;
        return new ReturnData(unCovered, coveredNoCamera, coveredHasCamera);
    }

    public enum Status{
        UNCOVERED, COVERED_NO_CAMERA, COVERED_HAS_CAMERA;
    }

    public static class ReturnData2{
        public Status status;
        public int cameras;

        public ReturnData2(Status status, int cameras) {
            this.status = status;
            this.cameras = cameras;
        }
    }

    public static int getMin2(Node head){
        ReturnData2 data = process2(head);
        return data.cameras + (data.status == Status.UNCOVERED ? 1 : 0);
    }

    public static ReturnData2 process2(Node node){
        if (node == null){
            return new ReturnData2(Status.COVERED_NO_CAMERA, 0);
        }
        ReturnData2 left = process2(node.left);
        ReturnData2 right = process2(node.right);
        int cameras = left.cameras + right.cameras;
        if (left.status == Status.UNCOVERED || right.status == Status.UNCOVERED){
            return new ReturnData2(Status.COVERED_HAS_CAMERA, cameras + 1);
        }
        if (left.status == Status.COVERED_HAS_CAMERA && right.status == Status.COVERED_HAS_CAMERA){
            return new ReturnData2(Status.COVERED_NO_CAMERA, cameras);
        }
        return new ReturnData2(Status.UNCOVERED, cameras);
    }
}

你可能感兴趣的:(数据结构)