贪心算法部分

谈心算法核心是拿局部最优逐渐实现整体的最优,没有固定模版,对于具体问题要看能不能按照谈心算法的思想实现

 这一题的思路可分别从满足大胃口和小胃口入手,这里用到双指针,避免两个for循环。让数组先从小到大排序,然后按照从小到大、从左到右比较的顺序来进行比较

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        int res = 0;//记录满足的数量
        Arrays.sort(g);
        Arrays.sort(s);
        for (int i = 0,j = 0;i < g.length && j < s.length;) {
            if (g[i] > s[j]) j++;
            else if (g[i] <= s[j]) {
                ++res;
                ++i;
                ++j;
            }
        }
        return res;
    }
}

 

关键是怎样利用谈心思想找到数组的波峰波谷

class Solution {
    public int wiggleMaxLength(int[] nums) {
       //利用树形结合,将数据的分布画出,每一个使得前后相邻差值发生变化的元素,就可以使得节点数加1
       int resu = 1;//默认一个元素可以行成一个摆动
       int preDiff =0;//默认其实点的diff值为0
       int curDiff = 0;
       for(int i = 0; i < nums.length -1; ++i) {
           //本质是从左到右找极值点
           curDiff = nums[i+1] - nums[i];//这样可以解决前两个点,这样每次得到的i都是位于波峰还有波谷
           if (curDiff > 0 && preDiff <=0 || curDiff < 0 && preDiff >=0) {
               resu++;
               preDiff = curDiff;//异号就进行交换
           }
       }
       return resu;
    }
}

 

贪心算法在这里要的是局部数组之和大于0的部分,使得结果转为0的节点必然不是,接着从下一个大于0的节点开始记录就可以

class Solution {
    public int maxSubArray(int[] nums) {
        //记录遍历过程中的累加和,当出现结果为负数时就清零,从下一个节点重新开始计算
        int max = Integer.MIN_VALUE;
        int count=0;
        for (int i = 0; i < nums.length; ++i) {
            count += nums[i];
            max = count > max ? count : max;
            if (count < 0) count=0;//说明nums【i】为负数,那么最大子数组一定不包含该节点。从下一个大于0的节点重新开始探索。
        }
        return max;
    }
}

 

 注意核心方法是找到一个排序的规则,然后按照规则来选择相对应的对象从而得到结果。

这一题参考左程云的贪心算法讲解。

class Solution {
    public static class Node {
        int p;
        int c;

        public Node(int p, int c) {
            this.p = p;
            this.c = c;
        }
    }

    public static class MinCostComparator implements Comparator<Node> {

        @Override
        public int compare(Node o1, Node o2) {
            return o1.c - o2.c;
        }

    }

    public static class MaxProfitComparator implements Comparator<Node> {

        @Override
        public int compare(Node o1, Node o2) {
            return o2.p - o1.p;
        }

    }

    public static int findMaximizedCapital(int k, int w, int[] profits, int[] capital) {
        PriorityQueue<Node> minCostQ = new PriorityQueue<>(new MinCostComparator());
        PriorityQueue<Node> maxProfitQ = new PriorityQueue<>(new MaxProfitComparator());
        //将所有项目按照花费从小到大扔到小根堆中
        for (int i = 0; i < profits.length; ++i) {
            minCostQ.add(new Node(profits[i], capital[i]));
        }
        //接着按照能够本钱往大顶堆中放入能够承接的项目,选择最顶上利润最大的来做
        for (int i = 0; i < k; ++i) {
            while (!minCostQ.isEmpty() && minCostQ.peek().c <= w) {
                maxProfitQ.add(minCostQ.poll());
            }
            if (maxProfitQ.isEmpty()) {//如果没有项目可以做
                return w;
            }
            w += maxProfitQ.poll().p;
        }
        return w;
    }
}

关键是从第一个点开始的跳动位置范围是否包含住数组的结尾。根据for循环结果来更改i的界限,那么对于起始点为0的也能有效去除

class Solution {
    public boolean canJump(int[] nums) {
        //通过倒序来进行判断
        if (nums.length == 1) return true;
        int res = 0;
        for (int i = 0; i <= res; ++i) {//for循环界限可以变化
            res = Math.max(i + nums[i], res);
            if (res >= nums.length - 1) return true;
        }
        return false;
    }
}

 

 这一题也是贪心算法的很好体现,先尽量让所有能转为正数的数转为正数,然后求和,之后再次对数组进行排序,将最小值放在数组头部,根据k剩余情况进行判断。看leetcode下面的解析,思路比代码随想录的还要棒

class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        Arrays.sort(nums);//将数组从小到大排列
        int sum = 0;
        for (int i = 0 ; i < nums.length ; ++i) {//把能转的负数都转为正数
            if (nums[i] < 0 && k > 0) {
                nums[i] = -nums[i];
                k--;
            }
            sum += nums[i];
        }
        Arrays.sort(nums);//这里把数组剩下最小的数放在头部
        //如果k已经用完,那么结果就是sum
        //如果k没有用完,剩余的k为偶数,那么结果仍未sum,为奇数,那么将nums[0]变为负值,结果减去其2倍就可以
        return sum = (k % 2) == 0 ? sum : (sum - 2 * nums[0]);
    }
}

参考之前做过的一道题,如果总体油耗满足能够跑满一圈,找开始位置,就是看前面的累加之和,如果有小于0的情况,那么满足要求的点必然在该点之后,知道找到最后损耗之和始终大于0的起始点

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        //根据线总体再局部的思路,从头开始如果出现累加和小于0,那么开始的位置取下一个,知道下一个是的当前的消耗大于0
        int start = 0, curSum = 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;
    }
}

 

关键是对每次收到钱的找零判断,要注意总体能找0,但是过程有错误的地方

class Solution {
    public boolean lemonadeChange(int[] bills) {
        //记录将10块钱找情之后的5、10、20剩余数量
        int m = 0, n = 0, q = 0;
        for (int i = 0; i < bills.length; ++i) {
            if (bills[i] == 5) ++m;
            if (bills[i] == 10) {
                ++n;
                --m;
                if (m < 0) return false;
            }
            if (bills[i] == 20) {
                ++q;
                //20元的找零要根据剩余钱的情况进行判断,5元10元都有的情况,一定能完成一次找零
                //只有5元的情况,只有5元数量大于3张才能完成找零
                //剩余情况都为不能实现,所以要尽可能在遍历完成之前判断出对错,不然会出现过程中不能找零,但是最终可以实现的错误情况
                if (m > 0 && n >0) {
                    --m;
                    --n;
                // } else if (m > 0 && n == 0) {
                //     if (m / 3 >= 1) {
                //         m -= 3;
                //     } else return false;   
                // } else return false;
                } else if (m / 3 >= 1) {
                    m -= 3;
                } else return false;
            }
        }
        return true;
    }
}

对于这种考虑双重因素的排序,一般按照其中一个升序,再按照一中一个降序,这样可以减少交换的次数,这一题把身高按照降序排列,这样排在前面的都是高于后面的,之后再链表中重新在索引位置插入元素就可以

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        //将people里面的数据在身高相同时按照身高的ki升序排列,不相同时按照身高降序
        Arrays.sort(people, (o1, o2) -> {
            if (o1[0] == o2[0]) return o1[1] - o2[1];
            else return o2[0] - o1[0];
        });
        LinkedList<int[]> list = new LinkedList<>();//list可以再指定索引位置出添加
        for (int[] p : people) {
            list.add(p[1], p);
        }
        return list.toArray(new int[people.length][]);//二维数组的创建
    }
}

还是两个指标的排序选择,然后对于边界end的更新,这里对于两个数的比较,要注意对于int的边界值比较,最好用integer.compare()来比较

class Solution {
    public int findMinArrowShots(int[][] points) {
        //涉及到两个指标,那么就牵扯到按照其中一个元素排序,这里按照start
        //这里注意如果两个球不重叠,那么就要多射一键,当两个气球重叠时,将重叠的边界按照两个气球最小的右边界来取
        Arrays.sort(points, (o1, o2) ->  Integer.compare(o1[0],o2[0]));
        //这里注意如果使用o1[0]-o2[0],对于极限边界值没法判断
        int count = 1;//因为至少有一个气球,至少要射一箭
        for (int i = 1; i < points.length; ++i) {
            if (points[i][0] > points[i-1][1]){//两个气球不重叠
                count++;
            } else {//两个气球重叠,更新第i个球的end边界,取两个球的最小值
                //如果下一个球的satrt大于这个end那么需要再加一箭,否则重叠
                points[i][1] = Math.min(points[i][1], points[i-1][1]);
            }
        }
        return count;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值