0
点赞
收藏
分享

微信扫一扫

【算法笔记】汇总——贪心篇

绣文字 2022-02-05 阅读 24

【算法笔记】汇总——贪心篇

本篇内容的主旨在于总结LeetCode中常见的贪心题涉及的基本内容,并对此做出一定的总结与归纳,算是笔者心路历程的一些许感悟。
首先,我们将贪心题按难易程度划分为如下情况:

贪心简单题

以下三道题目就是简单题,大家会发现贪心感觉就是常识。是的,如下三道题目,就是靠常识,分析出局部最优是什么,全局最优是什么,贪心贪的也就有理有据!
贪心算法:分发饼干
贪心算法:K次取反后最大化的数组和
贪心算法:柠檬水找零

贪心中等题

贪心中等题,靠常识可能就有点想不出来了。开始初现贪心算法的难度与巧妙之处。
贪心算法:摆动序列
贪心算法:单调递增的数字

    public int monotoneIncreasingDigits(int n) {
        if (n == 0) {
            return 0;
        }
        char[] arr = Integer.toString(n).toCharArray();
        int start = Integer.MAX_VALUE;
        for (int i = arr.length - 1; i >= 1; i--) {
            if (arr[i - 1] > arr[i]) {
                start = i;
                arr[i - 1]--;
            }
        }
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == '0' && i == 0) {
                continue;
            }
            if (i >= start) {
                res.append('9');
            } else {
                res.append(arr[i]);
            }
        }
        return Integer.parseInt(res.toString());
    }

贪心解决股票问题

大家都知道股票系列问题是动规的专长,其实用贪心也可以解决,而且还不止就这两道题目,但这两道比较典型,我就拿来单独说一说
贪心算法:买卖股票的最佳时机II
贪心算法:买卖股票的最佳时机含手续费

两个维度权衡问题

在出现两个维度相互影响的情况时,两边一起考虑一定会顾此失彼,要先确定一个维度,再确定另一个一个维度。
贪心算法:分发糖果

    public int candy(int[] ratings) {
        int n = ratings.length;
        int[] candyCnt = new int[n];
        Arrays.fill(candyCnt, 1);
        for (int i = 1; i < n; i++) {
            if (ratings[i] > ratings[i - 1]) {
                candyCnt[i] = candyCnt[i - 1] + 1;
            }
        }

        for (int i = n - 2; i >= 0; i--) {
            if (ratings[i] > ratings[i + 1]) {
                candyCnt[i] = Math.max(candyCnt[i], candyCnt[i + 1] + 1);
            }
        }
        return Arrays.stream(candyCnt).sum();
    }

贪心算法:根据身高重建队列

    public int[][] reconstructQueue(int[][] people) {
        Arrays.sort(people, (a, b) -> {
            if (a[0] == b[0]) {
                return a[1] - b[1];
            }
            return b[0] - a[0];
        });
        LinkedList<int[]> queue = new LinkedList<>();
        for (int[] p : people) {
            queue.add(p[1], p);
        }
        return queue.toArray(new int[people.length][]);
    }

在讲解本题的过程中,还强调了编程语言的重要性,模拟插队的时候,使用list(链表)替代了vector(动态数组),效率会高很多。
大家也要掌握自己所用的编程语言,理解其内部实现机制,这样才能写出高效的算法!

贪心难题

这里的题目如果没有接触过,其实是很难想到的,甚至接触过,也一时想不出来,所以题目不要做一遍,要多练!

贪心解决区间问题

关于区间问题,大家应该印象深刻,有一周我们专门讲解的区间问题,各种覆盖各种去重。
贪心算法:跳跃游戏
贪心算法:跳跃游戏II
贪心算法:用最少数量的箭引爆气球

    public int findMinArrowShots(int[][] points) {
        if (points.length == 0) {
            return 0;
        }
        Arrays.sort(points, (a, b) -> {
            return Integer.compare(a[0], b[0]);
        });
        int res = 1;
        for (int i = 1; i < points.length; i++) {
            if (points[i - 1][1] < points[i][0]) {
                res++;
            } else {
                points[i][1] = Math.min(points[i][1], points[i - 1][1]);
            }
        }
        return res;
    }

贪心算法:无重叠区间

    public int eraseOverlapIntervals(int[][] intervals) {
        if (intervals.length < 2) {
            return 0;
        }

        Arrays.sort(intervals, (a, b) -> {
            if (a[1] != b[1]) {
                return Integer.compare(a[1], b[1]);
            } else {
                return Integer.compare(a[0], b[0]);
            }
        });

        int res = 1, end = intervals[0][1];
        for (int i = 1; i < intervals.length; i++) {
            if (intervals[i][0] >= end) {
                res++;
                end = intervals[i][1];
            }
        }
        return intervals.length - res;
    }

贪心算法:划分字母区间
贪心算法:合并区间

    public int[][] merge(int[][] intervals) {
        Arrays.sort(intervals, (a, b) -> {
            return a[0] - b[0];
        });
        ArrayList<int[]> res = new ArrayList<>();
        res.add(intervals[0]);
        int last = intervals[0][1];
        for (int i = 1; i < intervals.length; i++) {
            if (intervals[i][0] <= last) {
                int[] tmp = res.get(res.size() - 1);
                tmp[1] = Math.max(tmp[1], intervals[i][1]);
                last = tmp[1];
            } else {
                res.add(intervals[i]);
                last = intervals[i][1];
            }

        }
        return res.toArray(new int[res.size()][]);
    }
其他难题

贪心算法:最大子序和 其实是动态规划的题目,但贪心性能更优,很多同学也是第一次发现贪心能比动规更优的题目。
贪心算法:加油站可能以为是一道模拟题,但就算模拟其实也不简单,需要把while用的很娴熟。但其实是可以使用贪心给时间复杂度降低一个数量级。


    public int canCompleteCircuit(int[] gas, int[] cost) {
        int curSum = 0, start = 0, totalSum = 0;
        for (int i = 0; i < gas.length; i++) {
            curSum += gas[i] - cost[i];
            totalSum += gas[i] - cost[i];
            if (curSum < 0) {
                start = i + 1;
                curSum = 0;
            }
        }
        if (totalSum < 0) {
            return -1;
        }
        return start;
    }

最后贪心系列压轴题目贪心算法:我要监控二叉树,不仅贪心的思路不好想,而且需要对二叉树的操作特别娴熟,这就是典型的交叉类难题了。

    int cnt = 0;

    public int minCameraCover(TreeNode root) {
        if (trval(root) == 0) {
            cnt++;
        }
        return cnt;
    }

    /**
     * 0:无覆盖,1:摄像头,2:有覆盖
     *
     * @param root
     * @return
     */
    private int trval(TreeNode root) {
        if (root == null) {
            return 2;
        }
        int left = trval(root.left);
        int right = trval(root.right);

        if (left == 2 && right == 2) {
            return 0;
        }
        if (left == 0 || right == 0) {
            cnt++;
            return 1;
        }
        if (left == 1 || right == 1) {
            return 2;
        }
        return 0;
    }
举报

相关推荐

0 条评论