前置知识: 对数器 、基础排序、有序表、比较器、堆结构

核心思想

狭义的贪心: 每一步都做出在当前状态下最好或最优的选择,从而希望最终的结果是最好或最优的算法

广义的贪心: 通过分析题目自身的特点和性质,只要发现让求解答案的过程得到加速的结论,都算广义的贪心

贪心是最符合自然智慧的思想,一般分析门槛不高 理解基本的排序、有序结构,有基本的逻辑思维就能理解 但是贪心的题目,千题千面,极难把握 难度在于证明局部最优可以得到全局最优,好在!我们有对数器!

有关贪心的若干现实 & 提醒

1、不要去纠结严格证明,每个题都去追求严格证明,浪费时间、收益很低,而且千题千面。玄学!

2、一定要掌握用对数器验证的技巧,这是解决贪心问题的关键

3、解法几乎只包含贪心思路的题目,代码量都不大

4、大量累积贪心的经验,重点不是证明,而是题目的特征,以及贪心方式的特征,做好总结方便借鉴

5、关注题目数据量,题目的解可能来自贪心,也很可能不是,如果数据量允许,能不用贪心就不用(稳)

6、贪心在笔试中出现概率不低,但是面试中出现概率较低,原因是 淘汰率 vs 区分度

7、广义的贪心无所不在,可能和别的思路结合,一般都可以通过自然智慧想明白,依然不纠结证明

例题

1、最大数

题目描述: 给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。

注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。

示例 1:

输入:nums = [10,2] 输出:"210"

示例 2:

输入:nums = [3,30,34,5,9] 输出:"9534330"

解题思路:

  • 这个题的最后返回结果是字符串,同时它的组合也是按照字符串的组合来的

  • 最后的要求是组成一个最大整数,而按照字符串的角度来看就是组合出一个最大字典序的字符串

  • 而字符串的组合无论是字典序最大还是字典序最小,都不只是将大/小字符排在前面,而是组合后的大/小的排在前面,重点是组合,

  • 因此这里总结这里的贪心策略,是 Arrays.sort(strs,(a,b)->(b+a).compareTo(a+b));, 这是最大字典序,如果想要最小字典序,则为 Arrays.sort(strs,(a,b)->(a+b).compareTo(b+a));

  • 不需要知道证明,只需要知道这种类型就是这样的一个贪心策略,对数其验证正确即可

项目名称

内容填写(每道题一行填写)

📘 题目编号 / 标题

LeetCode 179 + 最大数

🧠 题目关键词(原文关键词)

每个数的顺序(每个数不可拆分)使之组成一个最大的整数。

🧩 抽象问题类型(题目本质)

组合字典序最大/小模型

🔍 数据规模 / 限制

- 1 <= nums.length <= 100 - 0 <= nums[i] <= 109

🧭 我的初步思路

无想法

✅ 正确解法类型

贪心

❗ 没想到的原因

没见过这种贪心策略

📦 归入的题型分类

贪心

🧠 触发词(以后遇到就联想)

数不可拆分组合成最大整数->关于最大/小字典序的贪心

🧪 解法一句话总结

构造最大字典序数组 Arrays.sort(strs,(a,b)->(b+a).compareTo(a+b));,

代码如下

 package main.java.class089;  
   
 import java.util.ArrayList;  
 import java.util.Arrays;  
   
 /**  
  * @program: ZuoChengxunAlgorithmClass  
  * @ClassName Code01_LargestNumber  
  * @description: 最大数  
  * // 给定一组非负整数nums  
  * // 重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数  
  * // 测试链接 : https://leetcode.cn/problems/largest-number/  
  * @author: zs宝  
  * @create: 2025-10-21 09:51  
  * @Version 1.0  
  **/public class Code01_LargestNumber {  
     // strs中全是非空字符串,要把所有字符串拼接起来,形成字典序最小的结果  
     // 暴力方法  
     // 为了验证  
     // 生成所有可能的排列  
     // 其中选出字典序最小的结果  
     public static String way1(String[] strs) {  
         ArrayList<String> ans = new ArrayList<>();  
         f(strs, 0, ans);  
         ans.sort((a, b) -> a.compareTo(b));  
         return ans.get(0);  
     }  
   
     // 全排列代码,讲解038,常见经典递归过程解析  
     public static void f(String[] strs, int i, ArrayList<String> ans) {  
         if (i == strs.length) {  
             StringBuilder path = new StringBuilder();  
             for (String s : strs) {  
                 path.append(s);  
             }  
             ans.add(path.toString());  
         } else {  
             for (int j = i; j < strs.length; j++) {  
                 swap(strs, i, j);  
                 f(strs, i + 1, ans);  
                 swap(strs, i, j);  
             }  
         }  
     }  
   
     public static void swap(String[] strs, int i, int j) {  
         String tmp = strs[i];  
         strs[i] = strs[j];  
         strs[j] = tmp;  
     }  
   
     // strs中全是非空字符串,要把所有字符串拼接起来,形成字典序最小的结果  
     // 正式方法  
     // 时间复杂度O(n*logn)  
     public static String way2(String[] strs) {  
         Arrays.sort(strs, (a, b) -> (a + b).compareTo(b + a));  
         StringBuilder path = new StringBuilder();  
         for (int i = 0; i < strs.length; i++) {  
             path.append(strs[i]);  
         }  
         return path.toString();  
     }  
   
     // 为了验证  
     // 生成长度1~n的随机字符串数组  
     public static String[] randomStringArray(int n, int m, int v) {  
         String[] ans = new String[(int) (Math.random() * n) + 1];  
         for (int i = 0; i < ans.length; i++) {  
             ans[i] = randomString(m, v);  
         }  
         return ans;  
     }  
   
     // 为了验证  
     // 生成长度1~m,字符种类有v种,随机字符串  
     public static String randomString(int m, int v) {  
         int len = (int) (Math.random() * m) + 1;  
         char[] ans = new char[len];  
         for (int i = 0; i < len; i++) {  
             ans[i] = (char) ('a' + (int) (Math.random() * v));  
         }  
         return String.valueOf(ans);  
     }  
   
     // 为了验证  
     // 对数器  
     public static void main(String[] args) {  
         int n = 8; // 数组中最多几个字符串  
         int m = 5; // 字符串长度最大多长  
         int v = 4; // 字符的种类有几种  
         int testTimes = 2000;  
         System.out.println("测试开始");  
         for (int i = 1; i <= testTimes; i++) {  
             String[] strs = randomStringArray(n, m, v);  
             String ans1 = way1(strs);  
             String ans2 = way2(strs);  
             if (!ans1.equals(ans2)) {  
                 // 如果出错了  
                 // 可以增加打印行为找到一组出错的例子  
                 // 然后去debug  
                 System.out.println("出错了!");  
             }  
             if (i % 100 == 0) {  
                 System.out.println("测试到第" + i + "组");  
             }  
         }  
         System.out.println("测试结束");  
     }  
   
   
     // 测试链接 : https://leetcode.cn/problems/largest-number/    public String largestNumber(int[] nums) {  
         int n=nums.length;  
         //将数值转为字符串  
         String[]strs=new String[n];  
         for(int i=0;i<n;i++){  
             strs[i]=String.valueOf(nums[i]);  
         }  
         //排序,按最大字典序来  
         Arrays.sort(strs,(a,b)->(b+a).compareTo(a+b));  
         if(strs[0].equals("0")){  
             return "0";  
         }  
         StringBuilder ans=new StringBuilder();  
         for(int i=0;i<n;i++){  
             ans.append(strs[i]);  
         }  
         return ans.toString();  
     }  
   
 }

2、两地调度

题目描述: 公司计划面试 2n 人。给你一个数组 costs ,其中 costs[i] = [aCosti, bCosti] 。第 i 人飞往 a 市的费用为 aCosti ,飞往 b 市的费用为 bCosti 。

返回将每个人都飞到 a 、b 中某座城市的最低费用,要求每个城市都有 n 人抵达

示例 1:

输入:costs = [[10,20],[30,200],[400,50],[30,20]] 输出:110 解释: 第一个人去 a 市,费用为 10。 第二个人去 a 市,费用为 30。 第三个人去 b 市,费用为 50。 第四个人去 b 市,费用为 20。

最低总费用为 10 + 30 + 50 + 20 = 110,每个城市都有一半的人在面试。

示例 2:

输入:costs = [[259,770],[448,54],[926,667],[184,139],[840,118],[577,469]] 输出:1859

示例 3:

输入:costs = [[515,563],[451,713],[537,709],[343,819],[855,779],[457,60],[650,359],[631,42]] 输出:3086

提示:

  • 2 * n == costs.length

  • 2 <= costs.length <= 100

  • costs.length 为偶数

  • 1 <= aCosti, bCosti <= 1000

解题思路:

  • 记住这种类似场景的贪心

    • 将数组非为两类,一类要多少去某处,一类要多少去另一处,求代价最小的分配

    • 贪心的策略为:

      • 先全部都去某一处A,求出总代价sum,同时构造辅助数组arr,记录每个人去另一处B的代价与去去当前处A代价的差值

      • 将辅助数组排序,题目要求去B处的数量为m, 取arr中前m个的累加和,然后与sum相加

        • 为什么是相加

        • 因为sum=很多a相加,arr中是每一个b-a

        • 现在取了前m个arr的数,这些都是去b相比去a最好的那几个,于是sum=a+b-a=b

        • 这样的过程,减少了区分那些人去A,那些人去B的分开计算,直接一步得到最终结果

项目名称

内容填写(每道题一行填写)

📘 题目编号 / 标题

LeetCode 1029 + 两地调度

🧠 题目关键词(原文关键词)

将每个人都飞到 a 、b 中某座城市的最低费用,要求每个城市都有 n 人抵达

🧩 抽象问题类型(题目本质)

分配问题,甚至和01背包很像

🔍 数据规模 / 限制

- 2 * n == costs.length - 2 <= costs.length <= 100 - costs.length 为偶数 - 1 <= aCosti, bCosti <= 1000

🧭 我的初步思路

无想法

✅ 正确解法类型

贪心

❗ 没想到的原因

没想出贪心的策略

📦 归入的题型分类

贪心类

🧠 触发词(以后遇到就联想)

分配目的地只有2个->贪心

🧪 解法一句话总结

对每一个人去两地代价的差值做排序,选出去某地最适合的m个人

代码如下

 package main.java.class089;  
   
 import java.util.Arrays;  
   
 /**  
  * @program: ZuoChengxunAlgorithmClass  
  * @ClassName Code02_TwoCityScheduling  
  * @description: 两地调度  
  * // 公司计划面试2n个人,给定一个数组 costs  
  * // 其中costs[i]=[aCosti, bCosti]  
  * // 表示第i人飞往a市的费用为aCosti,飞往b市的费用为bCosti  
  * // 返回将每个人都飞到a、b中某座城市的最低费用  
  * // 要求每个城市都有n人抵达  
  * // 测试链接 : https://leetcode.cn/problems/two-city-scheduling/  
  * @author: zs宝  
  * @create: 2025-10-21 10:17  
  * @Version 1.0  
  **/public class Code02_TwoCityScheduling {  
     public int twoCitySchedCost(int[][] costs) {  
         int n=costs.length;  
         int[]arr=new int[n];  
         int sum=0;  
         //全走a  
         for(int i=0;i<n;i++){  
             sum+=costs[i][0];  
             arr[i]=costs[i][1]-costs[i][0];  
         }  
         Arrays.sort(arr);  
         //一半人去a,一半人去b  
         //上一轮全走的a,这一轮找出去b更划算的前一半人  
         for(int i=0;i<n/2;i++){  
             //注意是加,sum=b-a+a-->就是走的b  
             sum+=arr[i];  
         }  
         return sum;  
     }  
 }

3、吃掉N个橘子的最少天数

题目描述: 厨房里总共有 n 个橘子,你决定每一天选择如下方式之一吃这些橘子:

  • 吃掉一个橘子。

  • 如果剩余橘子数 n 能被 2 整除,那么你可以吃掉 n/2 个橘子。

  • 如果剩余橘子数 n 能被 3 整除,那么你可以吃掉 2*(n/3) 个橘子。

每天你只能从以上 3 种方案中选择一种方案。

请你返回吃掉所有 n 个橘子的最少天数。

示例 1:

输入:n = 10 输出:4 解释:你总共有 10 个橘子。 第 1 天:吃 1 个橘子,剩余橘子数 10 - 1 = 9。 第 2 天:吃 6 个橘子,剩余橘子数 9 - 2(9/3) = 9 - 6 = 3。(9 可以被 3 整除) 第 3 天:吃 2 个橘子,剩余橘子数 3 - 2(3/3) = 3 - 2 = 1。 第 4 天:吃掉最后 1 个橘子,剩余橘子数 1 - 1 = 0。 你需要至少 4 天吃掉 10 个橘子。

示例 2:

输入:n = 6 输出:3 解释:你总共有 6 个橘子。 第 1 天:吃 3 个橘子,剩余橘子数 6 - 6/2 = 6 - 3 = 3。(6 可以被 2 整除) 第 2 天:吃 2 个橘子,剩余橘子数 3 - 2*(3/3) = 3 - 2 = 1。(3 可以被 3 整除) 第 3 天:吃掉剩余 1 个橘子,剩余橘子数 1 - 1 = 0。 你至少需要 3 天吃掉 6 个橘子。

示例 3:

输入:n = 1 输出:1

示例 4:

输入:n = 56 输出:6

提示:

  • 1 <= n <= 2*10^9

解题思路:

  • 运用dp表加速计算,避免重复的计算

  • 吃1个实则是为了辅助后面能够吃一半或者吃2/3,

  • 对于每一个数量的橘子,我们都先用吃1个将其辅助到能够吃一半与吃2/3的程度,比较那一种最佳,然后迭代吃

项目名称

内容填写(每道题一行填写)

📘 题目编号 / 标题

LeetCode 1553 + 吃掉N个橘子的最少天数

🧠 题目关键词(原文关键词)

- 吃掉一个橘子。 - 如果剩余橘子数 n 能被 2 整除,那么你可以吃掉 n/2 个橘子。 - 如果剩余橘子数 n 能被 3 整除,那么你可以吃掉 2*(n/3) 个橘子,吃掉所有 n 个橘子的最少天数

🧩 抽象问题类型(题目本质)

求状态 n 到 0 的最少转换步数

🔍 数据规模 / 限制

- 1 <= n <= 2*10^9

🧭 我的初步思路

贪心

✅ 正确解法类型

贪心

❗ 没想到的原因

具体怎么贪心

📦 归入的题型分类

贪心

🧠 触发词(以后遇到就联想)

吃掉橘子最少天数->贪心

🧪 解法一句话总结

利用吃一个去快速匹配能够吃一半或者2/3的程度,比较那种合适

代码如下:

 package main.java.class089;  
   
 import java.util.HashMap;  
 import java.util.Map;  
   
 /**  
  * @program: ZuoChengxunAlgorithmClass  
  * @ClassName Code03_MinimumNumberEatOranges  
  * @description: 吃掉N个橘子的最少天数  
  * // 厨房里总共有 n 个橘子,你决定每一天选择如下方式之一吃这些橘子  
  * // 1)吃掉一个橘子  
  * // 2) 如果剩余橘子数 n 能被 2 整除,那么你可以吃掉 n/2 个橘子  
  * // 3) 如果剩余橘子数 n 能被 3 整除,那么你可以吃掉 2*(n/3) 个橘子  
  * // 每天你只能从以上 3 种方案中选择一种方案  
  * // 请你返回吃掉所有 n 个橘子的最少天数  
  * // 测试链接 : https://leetcode.cn/problems/minimum-number-of-days-to-eat-n-oranges/  
  * @author: zs宝  
  * @create: 2025-10-21 10:41  
  * @Version 1.0  
  **/public class Code03_MinimumNumberEatOranges {  
     public static Map<Integer,Integer> dp=new HashMap<>();  
     public static int minDays(int n) {  
         if(n<=1){  
             return n;  
         }  
         if(dp.containsKey(n)){  
             return dp.get(n);  
         }  
         // 1) 吃掉一个橘子  
         // 2) 如果n能被2整除,吃掉一半的橘子,剩下一半  
         // 3) 如果n能被3正数,吃掉三分之二的橘子,剩下三分之一  
         // 因为方法2)和3),是按比例吃橘子,所以必然会非常快  
         // 所以,决策如下:  
         // 可能性1:为了使用2)方法,先把橘子吃成2的整数倍,然后直接干掉一半(1天),剩下的n/2调用递归  
         // 即,n % 2 + 1 + minDays(n/2)  
         // 可能性2:为了使用3)方法,先把橘子吃成3的整数倍,然后直接干掉三分之二,剩下的n/3调用递归  
         // 即,n % 3 + 1 + minDays(n/3)  
         // 至于方法1),完全是为了这两种可能性服务的,因为能按比例吃,肯定比一个一个吃快(显而易见的贪心)  
         int ans=Math.min(n%2+1+minDays(n/2),n%3+1+minDays(n/3));  
         dp.put(n,ans);  
         return ans;  
     }  
 }

4、会议室II

题目描述: 给你一个会议时间安排的数组 intervals 每个会议时间都会包括开始和结束的时间intervals[i]=[starti, endi] 返回所需会议室的最小数量

解题思路:

代码如下

 package main.java.class089;  
   
 import java.util.Arrays;  
 import java.util.PriorityQueue;  
   
 /**  
  * @program: ZuoChengxunAlgorithmClass  
  * @ClassName Code04_MeetingRoomsII  
  * @description: 会议室II  
  * // 给你一个会议时间安排的数组 intervals  
  * // 每个会议时间都会包括开始和结束的时间intervals[i]=[starti, endi]  
  * // 返回所需会议室的最小数量  
  * // 测试链接 : https://leetcode.cn/problems/meeting-rooms-ii/  
  * @author: zs宝  
  * @create: 2025-10-21 11:05  
  * @Version 1.0  
  **/public class Code04_MeetingRoomsII {  
     public static int minMeetingRooms(int[][] meeting) {  
         int n=meeting.length;  
         //以开始时间排序  
         Arrays.sort(meeting,(a,b)->a[0]-b[0]);  
         //小根堆  
         PriorityQueue<Integer> heap=new PriorityQueue<>();  
         int ans=0;  
         for(int i=0;i<n;i++){  
            while(!heap.isEmpty() && heap.peek()<=meeting[i][0]){  
                 heap.poll();  
             }  
             heap.add(meeting[i][1]);  
             ans=Math.max(ans,heap.size());  
         }  
         return ans;  
     }  
 }

5、课程表III

题目描述:

这里有 n 门不同的在线课程,按从 1 到 n 编号。给你一个数组 courses ,其中 courses[i] = [durationi, lastDayi] 表示第 i 门课将会 持续 上 durationi 天课,并且必须在不晚于 lastDayi 的时候完成。

你的学期从第 1 天开始。且不能同时修读两门及两门以上的课程。

返回你最多可以修读的课程数目。

示例 1:

输入:courses = [[100, 200], [200, 1300], [1000, 1250], [2000, 3200]] 输出:3 解释: 这里一共有 4 门课程,但是你最多可以修 3 门: 首先,修第 1 门课,耗费 100 天,在第 100 天完成,在第 101 天开始下门课。 第二,修第 3 门课,耗费 1000 天,在第 1100 天完成,在第 1101 天开始下门课程。 第三,修第 2 门课,耗时 200 天,在第 1300 天完成。 第 4 门课现在不能修,因为将会在第 3300 天完成它,这已经超出了关闭日期。

示例 2:

输入:courses = [[1,2]] 输出:1

示例 3:

输入:courses = [[3,2],[4,3]] 输出:0

提示:

  • 1 <= courses.length <= 104

  • 1 <= durationi, lastDayi <= 104

解题思路:

  • 这个题的贪心思路是,按照截至日期排序,越先截止的越先考虑是否完成

  • 若当前时间+持续时间<=截止日期,则选择当前课,并将持续时间放于大根堆中

  • 若当前时间+持续时>截止日期,则对当前课的持续时间与大根堆堆顶元素比较

    • 若当前课持续时间<大根堆堆顶元素,则取出堆顶元素,选择当前课持续时间,加入大根堆中

      • 理由,在当前截止时间上,我能让选择相同课程的数量花费的时间更小,也就意味着我后续有更大可能再多选一门课

  • 看见堆,知道思路,却不知道怎么coding, 我该如何才能弥补这块呢

项目名称

内容填写(每道题一行填写)

📘 题目编号 / 标题

LeetCode 630 + 课程表Ⅲ

🧠 题目关键词(原文关键词)

courses[i] = [durationi, lastDayi] 表示第 i 门课将会 持续 上 durationi 天课,并且必须在不晚于 lastDayi 的时候完成。不能同时修读两门及两门以上的课程,最多可以修读的课程数目

🧩 抽象问题类型(题目本质)

选出最多的任务,使每个任务都能在截止时间内完成。

🔍 数据规模 / 限制

- 1 <= courses.length <= 104 - 1 <= durationi, lastDayi <= 104

🧭 我的初步思路

贪心

✅ 正确解法类型

贪心

❗ 没想到的原因

有思路,但是不知道如何coding

📦 归入的题型分类

贪心类

🧠 触发词(以后遇到就联想)

选择最多课程->尝试贪心

🧪 解法一句话总结

如上解题思路部分

代码如下

 package main.java.class089;  
   
 /**  
  * @program: ZuoChengxunAlgorithmClass  
  * @ClassName Code05_CourseScheduleIII  
  * @description: 课程表III  
  * // 这里有n门不同的在线课程,按从1到n编号  
  * // 给你一个数组courses  
  * // 其中courses[i]=[durationi, lastDayi]表示第i门课将会持续上durationi天课  
  * // 并且必须在不晚于lastDayi的时候完成  
  * // 你的学期从第 1 天开始  
  * // 且不能同时修读两门及两门以上的课程  
  * // 返回你最多可以修读的课程数目  
  * // 测试链接 : https://leetcode.cn/problems/course-schedule-iii/  
  * @author: zs宝  
  * @create: 2025-10-21 11:28  
  * @Version 1.0  
  **/public class Code05_CourseScheduleIII {  
     class Solution {  
         public int scheduleCourse(int[][] courses) {  
             //以终止时间排序,终止时间越靠前的越先完成  
             //若有终止时间相同的,优先完成花费时间少的  
             Arrays.sort(courses,(a,b)->(a[1]-b[1]));  
             PriorityQueue<Integer> heap=new PriorityQueue<>((a,b)->(b-a));  
             int time=0;  
             for(int[] course:courses){  
                 if(course[0]+time<=course[1]){  
                     time+=course[0];  
                     heap.add(course[0]);  
                 }else{  
                     //当当前时间加上耗费时间超过最后期限时  
                     if(!heap.isEmpty() && heap.peek()>course[0]){  
                         time+=course[0]-heap.poll();  
                         heap.add(course[0]);  
                     }  
                 }  
             }  
             return heap.size();  
         }  
     }  
 }

6、连接棒材的最低费用

题目描述: 连接棒材的最低费用 你有一些长度为正整数的棍子 这些长度以数组sticks的形式给出 sticks[i]是第i个木棍的长度 你可以通过支付x+y的成本将任意两个长度为x和y的棍子连接成一个棍子 你必须连接所有的棍子,直到剩下一个棍子 返回以这种方式将所有给定的棍子连接成一个棍子的最小成本 测试链接 : https://leetcode.cn/problems/minimum-cost-to-connect-sticks/ 测试链接 : https://www.luogu.com.cn/problem/P1090

解题思路:

  • 大名鼎鼎的哈夫曼编码

代码如下

 package main.java.class089;  
   
 import java.util.PriorityQueue;  
   
 /**  
  * @program: ZuoChengxunAlgorithmClass  
  * @ClassName Code06_MinimumCostToConnectSticks1  
  * @description: 连接棒材的最低费用(leetcode测试)  
  * // 你有一些长度为正整数的棍子  
  * // 这些长度以数组sticks的形式给出  
  * // sticks[i]是第i个木棍的长度  
  * // 你可以通过支付x+y的成本将任意两个长度为x和y的棍子连接成一个棍子  
  * // 你必须连接所有的棍子,直到剩下一个棍子  
  * // 返回以这种方式将所有给定的棍子连接成一个棍子的最小成本  
  * // 测试链接 : https://leetcode.cn/problems/minimum-cost-to-connect-sticks/  
  * @author: zs宝  
  * @create: 2025-10-22 09:37  
  * @Version 1.0  
  **/public class Code06_MinimumCostToConnectSticks1 {  
     public static int connectSticks(int[] arr) {  
         //解提思路:大名鼎鼎的哈夫曼编码  
         //构建小根堆  
         PriorityQueue<Integer> heap=new PriorityQueue<>((a,b)->a-b);  
         for(int i=0;i< arr.length;i++){  
             heap.add(arr[i]);  
         }  
         int cur=0;  
         int sum=0;  
         while (heap.size()>1){  
             cur=heap.poll()+ heap.poll();  
             sum+=cur;  
             heap.add(cur);  
         }  
         return sum;  
     }  
 }

参考资料