基础算法:贪心算法

200 阅读3分钟

贪心算法

定义

贪心算法,又称贪婪算法。一种分级处理方法,是指在解决问题时,将完整的问题划分成多个步骤,认为只要每个步骤都是最优解(也就是局部最优解),最终得到的结果就是最优的。

实质上,贪心算法得到的并不是全局最优解,全局最优解需要采用动态规划算法。

思路

贪心算法一般按如下步骤进行:

  1. 建立数学模型来描述问题 。
  2. 把求解的问题分成若干个子问题 。
  3. 对每个子问题求解,得到子问题的局部最优解 。
  4. 把子问题的解局部最优解合成原来解问题的一个解

简易算法模型

Soulution() {
	建立数学模型
	拆解子问题
	找寻目标
	while(下一步还有子问题待解决){
		获得子问题的局部最优解
	}
	根据目标整合每一步的局部最优解推导至全局最优解
}

经典例题

股票买卖最佳时机

股票买卖最佳时机

image.png

问题分析

  • 建立模型

    • 变量:

      已知 n 天的股票价格 prices[0: n-1]

    • 操作:

      经过 n 天之后,其中第 i 天买入,第r天卖出的多轮操作,买入和卖出可以是同一天

    • 目标:

      获得最大的利润

  • 拆解成子问题

    为简化分析,把一对买入卖出操作简化为当天买入,第二天卖出。 (这一点非常重要)

    在不考虑手续费的情况下,比如 第1天买入,第3天卖出。

    实质上等同于 第一天买入,第二天卖出,第二天买入,第三天卖出。

  • 子问题的局部最优解

    因为问题简化为当天买入,隔天卖出,那么只要确定全部买入的时机即可。

    很明显,只要能盈利就是买入时机,盈利也就是 第二天价格 > 第一天 即可。

  • 局部是否可以推导成全局最优

​ 根据上面的拆解,只要每个操作都盈利,且所有能盈利的时机都操作了,那么可以认为即使从全局的角度来看,也实现了全部最优。

代码实现

 /**
     * 贪心算法计算最大利润(不考虑交易次数)
     * @param nums
     * @return
     */
    public static int maxProfit(int[] nums) {
        int count = 0;
        int length = nums.length -1;
        List<Integer> buyList = new ArrayList<>();
        for (int i = 0; i < length; i++) {
            int profit = Math.max(0, nums[i+1] - nums[i]);
            if (profit > 0) {
                count += profit;
                buyList.add(i);
            }
        }
        System.out.println(buyList);
        return count;
    }
	/**
     * 贪心算法计算最大利润(尽可能少的交易次数)
     * @param nums
     * @return
     */
    public static int maxProfitPro(int[] nums) {
        int count = 0;
        int length = nums.length -1;
        LinkedList<Integer> buyList = new LinkedList<>();
        for (int i = 0; i < length; i++) {
            int profit = Math.max(0, nums[i+1] - nums[i]);
            if (profit > 0 ) {
                count += profit;
                buyList.add(i);
            }
        }
        //
        int size = buyList.size();
        size = size -1;
        Set<Integer> result = new LinkedHashSet<>();
        Set<Integer> index = new LinkedHashSet<>();
        result.add(buyList.get(0));
        index.add(0);
        for (int i = 1; i < size; i++) {
            Integer pre = buyList.get(i - 1);
            Integer current = buyList.get(i);
            Integer next = buyList.get(i+1);
            if (index.contains(i-1)) {
                if (current-pre!=1) {
                    result.add(current);
                    index.add(i);
                }
            } else {
                if (next-current!=1) {
                    result.add(current);
                    index.add(i);
                }
            }
        }
        result.add(buyList.get(size));
        System.out.println(buyList);
        System.out.println(result);
        return count;
    }

测试结果

 public static void main(String[] args) {
        int[] nums = {1,5,6,9,2,4,6,6,10};
        int maxProfit = maxProfit(nums);
        System.out.println(maxProfit);

        int maxProfitPro = maxProfitPro(nums);
        System.out.println(maxProfitPro);
    }


//输出  
[0, 1, 2, 4, 5, 7]
16
[0, 1, 2, 4, 5, 7]
[0, 2, 4, 7]
16