Java 贪心算法经典问题解决

文章目录

    • 分金条
      • 题目
      • 思路
      • 代码实现
      • 测试用例以及结果输出
    • 花费资金做项目最大收益
      • 题目
      • 思路
      • 代码实现
      • 测试用例以及结果输出
    • 预定会议室
      • 题目
      • 思路
      • 代码实现
      • 测试用例以及结果输出
    • 取中位数
      • 题目
      • 思路
      • 代码实现
      • 测试用例以及结果输出
    • 最低字典序
      • 题目
      • 思路
      • 代码实现
      • 测试用例以及结果输出
    • 结语

分金条

题目

一块金条切成两半,是需要花费和长度数值一样的铜板的。比如 长度为20的 金条,不管切成长度多大的两半,都要花费20个铜 板。一群人想整分整块金 条,怎么分最省铜板?

例如,给定数组{10,20,30},代表一共三个人,整块金条长度为 10+20+30=60. 金条要分成10,20,30三个部分。 如果, 先把长 度60的金条分成10和50,花费60 再把长度50的金条分成20和30, 花费50 一共花费110铜板。 但是如果, 先把长度60的金条分成30和30,花费60 再把长度30 金条分成10和20,花费30 一共花费90铜板。

输入一个数组,返回分割的最小代价。

思路

哈夫曼树带权路径计算问题,更多了解可参考:哈夫曼树及其应用

  1. 先将给定数组进行排序,这里可以使用优先级队列处理【优先级堆结构】,将数组依次丢入优先级队列中;
  2. 每次从优先级队列中取出较小的值相加,记录计算结果,同时将结果重新丢入到队列中,直到队列中没有元素;
  3. 将过程中的所有计算结果相加,结果即为分割最小代价;

代码实现

   private static int lessMoney(int[] aar) {
        PriorityQueue<Integer> pQ = new PriorityQueue<>();
        for (int i = 0; i < aar.length; i++) {
            pQ.add(aar[i]);
        }
        int result = 0;
        int cur = 0;
        while (pQ.size() > 1) {
            cur = pQ.poll() + pQ.poll();
            result += cur;
            pQ.add(result);
        }
        return result;
    }

测试用例以及结果输出

  public static void main(String[] args) {
        int[] aar = new int[]{30, 10, 20};
        System.out.println(lessMoney(aar));
    }

输出结果:

90

花费资金做项目最大收益

题目

输入:
参数1,正数数组costs
参数2,正数数组profits
参数3, 正数k
参数4,正数m

其中,costs[i]表示i号项目的花费;profits[i]表示i号项目在扣除花费之后还能挣到的钱(利润);
k表示你不能并行、只能串行的最多做k个项目;
m表示你初始的资金;
说明:你每做完一个项目,马上获得的收益,可以支持你去做下一个 项目。
输出:你最后获得的最大钱数。

思路

基本原则:结合生活中的实际生产,每次选花费最小收益最高的项目去做,最终得到的收益肯定是最大的;

  1. 新定义Node类,包含每个项目对应的花费以及收益;
  2. 分别定义两个优先级队列,按照最小花费和最大收益优先级取元素;
  3. 将根据花费以及收益数组生成的Node数组丢入到最小花费优先级队列中;
  4. 进行K次遍历【最多可以做K个项目】,不断从最小花费优先级队列中取出项目,丢入到最大收益优先级队列中【注意资金问题】,在根据最大收益去做项目【即从最大收益队列中取项目做】,计算过程中获取的收益之和;

代码实现

/**
     * 计算最大收益
     *
     * @param k       表示能做K个项目
     * @param m       表示启动资金
     * @param profits 表示做每个项目去除花费后的利润
     * @param costs   表示做每个项目对应的花费
     * @return
     */
    private static int getMaxProfit(int k, int m, int[] profits, int[] costs) {
        //将项目对应花费以及收益包装成Node类,添加到Node[]数组中
        Node[] nodes = new Node[costs.length];
        for (int i = 0; i < costs.length; i++) {
            nodes[i] = new Node(costs[i], profits[i]);
        }
        // 最小花费优先级队列
        PriorityQueue<Node> minConstQ = new PriorityQueue<>(new MinComparator());
        // 最大收益优先级队列
        PriorityQueue<Node> maxProfitQ = new PriorityQueue<>(new MaxComparator());
        //添加到最小花费优先级队列中
        for (int i = 0; i < nodes.length; i++) {
            minConstQ.add(nodes[i]);
        }
        // k表示最多可以做k个项目
        for (int i = 0; i < k; i++) {
            //只要花费不超过启动资金,按照最小花费不断从队列中取,丢入到收益队列中
            while (!minConstQ.isEmpty() && minConstQ.peek().cost <= m) {
                maxProfitQ.add(minConstQ.poll());
            }
            //如果收益队列为空,就返回最终资金,否则每次从收益队列中取最大收益的项目去做;
            if (maxProfitQ.isEmpty()) {
                return m;
            }
            m = m + maxProfitQ.poll().profit;
        }
        return m;
    }

    private static class Node {
        /**
         * 花费
         */
        public int cost;
        /**
         * 利润
         */
        public int profit;

        public Node(int cost, int profit) {
            this.cost = cost;
            this.profit = profit;
        }
    }


    /**
     * 花费最小排序
     */
    private static class MinComparator implements Comparator<Node> {

        @Override
        public int compare(Node o1, Node o2) {
            return o1.cost - o2.cost; //>0表示o1>o2
        }
    }

    /**
     * 利润最大排序
     */
    private static class MaxComparator implements Comparator<Node> {

        @Override
        public int compare(Node o1, Node o2) {
            return o2.profit - o1.profit; // >0 表示o2>o1
        }
    }

测试用例以及结果输出

   public static void main(String[] args) {
        int k = 3;
        int m = 5;
        int[] profits = new int[]{1, 3, 4, 6, 8};
        int[] costs = new int[]{3, 6, 4, 2, 6};
        System.out.println(getMaxProfit(k, m, profits, costs));
    }

输出结果:

23

预定会议室

题目

一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲。

给你每一个项目开始的时间和结束的时间(给你一个数组,里面是一个个具体的项目),你来安排宣讲的日程,

要求会议室进行的宣讲的场次最多,返回这个最多的宣讲场次。

思路

优先做最早结束的项目,保证开始时间小于或等于要做项目的开始时间即可;

代码实现

 private static int getMaxProgram(Program[] program, int start) {
        Arrays.sort(program, new ProgramComparator());
        int result = 0;
        for (int i = 0; i < program.length; i++) {
            if (start <= program[i].start) {
                result++;
                start = program[i].end;
            }
        }
        return result;
    }

    /**
     * 定义项目会议  包含开始和结束时间
     */
    private static class Program {
        public int start;
        public int end;

        public Program(int start, int end) {
            this.start = start;
            this.end = end;
        }
    }

    /**
     * 按哪个项目先结束排序
     */
    private static class ProgramComparator implements Comparator<Program> {
        @Override
        public int compare(Program o1, Program o2) {
            return o1.end - o2.end;
        }
    }

测试用例以及结果输出

    public static void main(String[] args) {
        Program p1 = new Program(6, 10);
        Program p2 = new Program(7, 8);
        Program p3 = new Program(11, 13);
        Program p4 = new Program(13, 15);
        Program[] programs = new Program[]{p1, p2, p3, p4};
        System.out.println(getMaxProgram(programs, 6));
    }

输出结果:

3

取中位数

题目

一个数据流中,随时可以取得中位数;

思路

分别定义大根堆和小根堆,以下述逻辑进行存放和调整;

  1. 当大根堆为空时,元素直接添加到大根堆中;
  2. 当大根堆不为空时,如果元素小于或等于大根堆堆顶元素,则添加到大根堆中,否则添加到小根堆中;
  3. 当大根堆和小根堆元素个数相差为2时,需要进行堆调整,将元素个数多的堆堆顶元素放入元素个数少的堆中;
  4. 计算中位数,当大根堆和小根堆元素个数相等,则中位数为取两个堆的堆顶元素之和除以2,如果元素个数不相等,则中位数为元素个数多的堆的堆顶元素;

下面以以图进行举例说明,这里简单以队列表示大小根堆:

中位数计算
可以理解成通过堆对元素进行排序,只不过利用大小根队的性质,保证中位数可以通过堆顶数据进行计算得出,也避免了每次添加元素时进行排序问题,时间复杂度更低;

代码实现

    private static class MedianHelper {
        private PriorityQueue<Integer> minQ = new PriorityQueue<>(new MinComparator());
        private PriorityQueue<Integer> maxQ = new PriorityQueue<>(new MaxComparator());


        public int getMedian() {
            int maxQSize = maxQ.size();
            int minQSize = minQ.size();
            if (maxQSize == 0) {
                return 0;
            }
            //元素个数相等,取两者堆顶元素/2
            if (maxQSize == minQSize) {
                return (maxQ.peek() + minQ.peek()) / 2;
            }
            //元素个数不相等,取元素多的堆顶元素
            return maxQSize > minQSize ? maxQ.peek() : minQ.peek();
        }

		//插入元素
        public void addNum(int num) {
            if (maxQ.isEmpty()) {
                maxQ.add(num);
            } else {
                if (maxQ.peek() >= num) {
                    maxQ.add(num);
                } else {
                    minQ.add(num);
                }
            }
            modifyQSize();
        }


        /**
         * 调整两个堆的大小 一旦发现两个堆数据个数相差为2,则取多的丢到少的里面
         */
        private void modifyQSize() {
            int minQSize = minQ.size();
            int maxQSize = maxQ.size();
            if (minQSize - maxQSize == 2) {
                maxQ.add(minQ.poll());
            }
            if (maxQSize - minQSize == 2) {
                minQ.add(maxQ.poll());
            }
        }
    }

测试用例以及结果输出

    public static void main(String[] args) {
        int[] aar = new int[]{8, 6, 13, 10, 11, 19};
        MedianHelper helper = new MedianHelper();
        for (int i : aar) {
            helper.addNum(i);
        }
        System.out.println(helper.getMedian());

    }

输出结果:

10

最低字典序

题目

给定一个字符串类型的数组strs,找到一种拼接方式,使得把所有字符串拼起来之后形成的字符串具有最低的字典序。

思路

保证每次拼接后的字符串都是最低字典序的即可;

代码实现

 private static String lowestString(String[] strs) {
        if (strs == null || strs.length == 0) {
            return "";
        }
        Arrays.sort(strs, new LowestComparator());
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < strs.length; i++) {
            result.append(strs[i]);
        }
        return result.toString();
    }


    /**
     * 定义两个字符拼接最小字典序比较器
     */
    private static class LowestComparator implements Comparator<String> {

        @Override
        public int compare(String o1, String o2) {
            return (o1 + o2).compareTo(o2 + o1);
        }
    }

测试用例以及结果输出

    public static void main(String[] args) {
        String[] strs2 = {"b", "ab", "ac"};
        System.out.println(lowestString(strs2));
    }

结果输出:

abacb

结语

如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/43796.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

(20)操纵杆或游戏手柄

文章目录 前言 20.1 你将需要什么 20.2 校准 20.3 用任务规划器进行设置 20.4 飞行前测试控制装置 20.5 测试失控保护 20.6 减少控制的滞后性 前言 本文解释了如何用操纵杆或游戏手柄控制你的飞行器&#xff0c;使用任务计划器向飞行器发送"RC Override"消息…

【深入浅出 Yarn 架构与实现】 NodeManager 状态机管理

一、简介# NodeManager&#xff08;NM&#xff09;中的状态机分为三类&#xff1a;Application、Container 和 LocalizedResource&#xff0c;它们均直接或者间接参与维护一个应用程序的生命周期。 当 NM 收到某个 Application 的第一个 container 启动命令时&#xff0c;它会…

接口自动化测试-Python+Requests+Pytest+YAML+Allure配套撸码(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 接口自动化框架&a…

vue2项目迁移到vue3中的改动——基础积累

最近在跟着大神学习vue3的内容&#xff0c;发现之前vue2写的代码可以直接照搬到vue3中&#xff0c;但是有一些需要改动的内容&#xff0c;下面做一下记录。 1.定义对象时&#xff0c;需要指定每个属性值 例如&#xff1a;listQuery:{} 如果使用&#xff1a;listQuery.Filter…

chrome查看浏览器内核日志

由于经常在网页上调试播放音视频&#xff0c;但是总遇到一些未知原因&#xff0c;导致无法正常播放&#xff0c;亟需查看浏览器内核日志&#xff0c;分析原因&#xff0c;做一下笔记。 (1) 查看浏览器快捷键属性 &#xff08;2&#xff09;在快捷键启动位置补充参数 --enable-…

LabVIEW可重入VI,VI模板和动态VI之间的差异

LabVIEW可重入VI&#xff0c;VI模板和动态VI之间的差异 应该在何时使用可重入VI、模板VI和动态调用VI&#xff1f;这三种类型之间有什么区别&#xff1f; 可重入VI 当想要同时运行同一VI的多个实例时&#xff0c;将使用可重入VI。当VI不可重入时&#xff0c;VI只有一个数据空…

opencv对相机进行畸变矫正,及从矫正后的图像坐标反求原来的对应坐标

1.背景 目前有个项目&#xff0c;需要用到热成像相机。但是这个热成像相机它的畸变比较厉害&#xff0c;因此需要用标定板进行标定&#xff0c;从而消除镜头畸变。 同时需要实现用户用鼠标点击矫正后的画面后&#xff0c;显示用户点击位置的像素所代表的温度。 2.难点 消除镜…

11 spring-boot的MVC配置原理

11.1 spring-boot为MVC提供的自动配置 1.ContentNegotiatingViewResolver视图解析器&#xff1b; 2.静态资源或者支持WebJars&#xff1b; 3.自动注册类型转换器&#xff1a;比如说前台提交user的字段&#xff0c;后台自动封装的意思&#xff1b; 4.HttpMessageConverters&…

「苹果安卓」手机搜狗输入法怎么调整字体大小及键盘高度?

手机搜狗输入法怎么调整字体大小及键盘高度&#xff1f; 1、在手机上准备输入文字&#xff0c;调起使用的搜狗输入法手机键盘&#xff1b; 2、点击搜狗输入法键盘左侧的图标&#xff0c;进入更多功能管理&#xff1b; 3、在搜狗输入法更多功能管理内找到定制工具栏&#xff0c…

100天精通Golang(基础入门篇)——第17天:深入解析Go语言中的指针

&#x1f337; 博主 libin9iOak带您 Go to Golang Language.✨ &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &#x1f30a; 《I…

【MySQL】数据库基础

目录 一、什么是数据库 二、主流数据库 三、基本使用 3.1MySQL安装 3.2连接服务器 3.3服务器管理 3.4服务器&#xff0c;数据库&#xff0c;表关系 3.5使用案例 3.6数据逻辑存储 四、MySQL架构 五、SQL分类 六、存储引擎 6.1存储引擎 6.2查看存储引擎 6.3 存储引…

数学随想:轻量级算法服务。

数学随想&#xff1a;轻量级算法服务TOC 通常认为&#xff0c;数列是算法服务的基础。但是&#xff0c;真正用于算法服务的数列只有几个众所周知的基础数列。虽然对于不同的任务可以选择使用数列的一段用于服务&#xff0c;但是数列的使用还是复杂而繁重的。特别是在计算应用日…

Github上方导航栏介绍

Code Watch&#xff1a;相当于关注&#xff0c;到时候这个项目又有什么操作&#xff0c;就会以通知的形式提醒你。 Fork&#xff1a;也就是把这个项目拉到你的仓库里&#xff0c;之后你可以对该代码进行修改&#xff0c;之后你可以发起Pull Request&#xff0c;简称PR&#xf…

《数据分析-JiMuReport08》JiMuReport报表开发-报表列数量开发限制调整

JiMuReport报表开发列数量限制调整 1.开发列数限制 JiMuReport报表在开发的时候&#xff0c;需要100-200列的数据&#xff0c;但是在设计到一定数量的时候&#xff0c;水平下拉框就不能滑动了 2.报表参数调整 col: n 在application.yml文件的jmreport配置处&#xff0c;如果想…

【指针和数组笔试题(2)】详解指针、数组笔试题

文章目录 前言第一组题第二组题第三组题二维数组&#xff08;难点&#xff09;总结 前言 来到第二章&#xff0c;继续学习指针和数组笔试题 第一组题 #include<stdio.h> int main() {char arr[] "abcdef";//里面的放的是[a b c d e f \0]printf("%d\n&…

LeetCode45.Jump-Game-II<跳跃游戏II>

题目&#xff1a; 思路&#xff1a; 从上次大神那里获得的灵感 这题问的是次数,那么我们需要确保 1,能否跳到终点 2,得到次数. 第一次条获得的是nums[0],那么第一个数就是我们第一次能跳跃的范围.每次在范围里获得最大值.并且次数加一.然后进入下一次范围;即可得到次数; 代码…

Redis源码篇 - inset数据结构

inset是Redis中set类型的一种底层存储结构&#xff08;编码&#xff09;&#xff0c;它是基于整数数组来实现的&#xff0c;用于存储数值类型set集合数据&#xff0c;并具备长度可变、有序等特征。 有序性 为了方便查找&#xff0c;Redis会将intset中整数数据按照从小到大的顺…

电商系统架构设计系列(六):电商的「账户系统」设计要特别考虑哪些问题?

上篇文章中&#xff0c;我给你留了一个思考题&#xff1a;电商的账户系统&#xff0c;该如何设计&#xff1f; 今天这篇文章&#xff0c;我们来说一下电商的账户系统。 引言 账户系统负责记录和管理用户账户的余额&#xff0c;这个余额就是每个用户临时存在电商的钱&#xff…

Jenkins从配置到实战(二) - Jenkins如何在多台机器上自动化构建

前言 jenkins除了支持在本机上进行项目构建&#xff0c;还可以将构建任务分发到其他远程服务器上去执行&#xff0c;可以实现在不同平台和架构的机器上来完成项目的自动化构建任务&#xff0c;也能减轻jenkins服务器的压力。本文章就主要介绍下此流程。 准备工作 准备两台机…

LeetCode[剑指Offer51]数组中的逆序对

难度&#xff1a;Hard 题目&#xff1a; 在数组中的两个数字&#xff0c;如果前面一个数字大于后面的数字&#xff0c;则这两个数字组成一个逆序对。输入一个数组&#xff0c;求出这个数组中的逆序对的总数。 示例 1: 输入: [7,5,6,4] 输出: 5 限制&#xff1a; 0 < 数组…