贪心算法(又称贪婪算法)在对问题求解时,总是做出在当前看来是最好的选择
,也就是说,不从整体最优上加以考虑,它所做出的仅是在某种意义上的局部最优解
,从而最后得到全局最优。
贪心算法没有固定的算法框架,算法设计的关键是贪心策略
,必须注意的是,贪心算法不是对所有问题都能得到整体最优解,选择的贪心策略必须具备无后效性
,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。
贪心策略适用的前提是:局部最优策略能导致产生全局最优解
。
百度百科的解释:
此前
各状态及决策的影响,也就是说,未来与过去无关
。只看概念有点晦涩难懂,举个栗子吧(仅供理解):
所以有的路随便走
,OK,中午去上学对于下午去上学没有任何影响,这就是无后效性
,未来与过去无关。中午走过的路下午不能走
,OK,下午去上学走的路要收到中午上学走的路线的影响,这就不满足无后效性
,未来与过去有关。题目描述:
示例:
最多
有多少孩子可以吃饱的数量。Input: [1,2], [1,2,3] Output: 2
分析:
贪心策略,我们首先考虑饥饿度最小的孩子,这个孩子最容易吃饱
,我们给他最小的饼干,如果不能满足,就使用次大的饼干,这就是通过局部最优解,然后找到全局最优解先给数组进行排序
,然后在使用饼干数组中的元素和孩子数组中的元素对比,如果匹配成功就进行下一个孩子的投喂(这个时候两个数组的指针都要后移
),如果匹配失败就要使用饼干数组下一个元素对孩子进行投喂(这时候饼干数组后移,孩子数组不动
),直到匹配成功;代码实现:
public class Test01 { public static void main(String[] args) { int[] children=new int[]{1,2}; int[] cookies=new int[]{1,2,3}; int num=distribution(children,cookies); System.out.println(num); } public static int distribution(int[] children,int[] cookies ){ //使用工具类对数组进行排序 Arrays.sort(children); Arrays.sort(cookies); //定义两个索引【指针】 int indexCh=0; int indexCo=0; //注意:饼干数组一定会后移的,孩子数组如果投喂成功才会后移 while(indexCh<children.length&&indexCo<cookies.length){ if(indexCo>=indexCh){ indexCh++; indexCo++; }else { indexCo++; } } //最终返回的是投喂成功孩子的个数,索引每次都是投喂成功后先加一的,刚好是实际的个数 return indexCh; } }
题目描述:
[1,7,4,9,2,5]
是一个摆动序列,因为差值 [6,-3,5,-7,3]
是正负交替出现的。整数序列
,返回作为摆动序列的最长子序列的长度
,通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序
。示例:
输入: [1,7,4,9,2,5] 输出: 6 解释: 整个序列均为摆动序列。
输入: [1,17,5,10,13,15,10,5,16,8] 输出: 7 解释: 这个序列包含几个长度为 7 摆动序列,其中一个可为[1,17,10,13,10,16,8]
输入: [1,2,3,4,5,6,7,8,9] 输出: 2
分析:
nums=[1, 7, 4, 9, 11, 15, 5]
但是第四个元素比第三个元素大,第五个元素比第四个元素大,所以(贪心策略)下一个峰应该为第五个元素,更新lastIndex = 5
,这种情况除非一直或者一直减
。代码实现:
public static int wiggleMaxLength(int[] nums) { int n = nums.length; if (n < 2) { return n; } //因为初始不知道是峰还是谷,所以都设为1 int up = 1; int down = 1; //从索引为1的位置开始遍历 for (int i = 1; i < n; i++) { //峰,所以前面为谷,使用谷加1 if (nums[i] > nums[i - 1]) { up = down + 1; } //谷,所以前面为峰,使用峰加1 if (nums[i] < nums[i - 1]) { down = up + 1; } } return Math.max(up, down); }
题目描述:
最少个数
,起止相连不算重叠。示例:
Input:[[1,2],[2,4],[1,3]] Output:1
分析:
贪心策略,选择的区间结尾越小,余留给其它区间的空间就越大,就越能保留更多的区间
。因此,采用贪心算法,优先保留结尾小且不相交的区间。变量
保存当前已选定区间的结尾
,只要下个区间开头
不小于我们保存的区间的结尾
,我们就用这个区间的结尾去替换变量的值,用于下一次的比较。代码实现:
public static int region(int[][] arr) { //保留区间的个数 int num=1; /*Array.sort(array);来进行排序,如果我们数组中是放的基本数据类型,就可以直接比较大小排序, 如果我们放的是对象的话,这样排序就意义不大,需要我们自己进行相应的修改,得到我们想要的比较结果。 对二维数组按照区间的结尾大小排序,对于二维数组,我们可以重写这个比较器 使用二维数组中的一维数组的第二个元素来比较,也就是把二维数组中的一维数组看成一个元素 */ Arrays.sort(arr, new Comparator<int[]>() { public int compare(int[] o1, int[] o2) { return o1[1]-o2[1]; } }); //保存第一个保留区间的结尾 int temp=arr[0][1]; for(int i=1;i<arr.length;i++){ /*我们使用结尾进行排序,我们要尽可能选择结尾小的区间,这样才能留下更多的区间, 我们先用一个`变量`保存当前`已选定区间的结尾`,只要下个区间`开头`不小于我们保存的区间的`结尾`, 我们就用这个区间的结尾去替换变量的值,用于下一次的比较*/ if(temp<=arr[i][0]){ num++; temp=arr[i][1]; } } return arr.length-num; }
题目描述:
你必须在再次购买前出售掉之前的股票
)。示例:
输入: p=[2,4,1] 输出: 2 解释: 在第1天(股票价格 = 2)的时候买入,在第2天(股票价格 = 4)的时候卖出,这笔交易所能获得利润 4-2 = 2 。
输入: p=[3,2,6,5,1,3] 输出: 6 解释: 在第2天(股票价格 = 2)的时候买入,在第3天(股票价格 = 6)的时候卖出,这笔交易所能获得利润6-2 =4 在第5天(股票价格 = 1)的时候买入,在第6天 (股票价格=3) 的时候卖出, 这笔交易所能获得利润 = 3-1 =2 。
分析
代码实现
public static int maxProfit(int[] arr) { //开始利润为0 int profit=0; //循环遍历数组 for(int i=1;i<arr.length;i++){ //如果后一天的价格比前一天的高,就在前一天买入 if(arr[i]-arr[i-1]>=0){ profit+=arr[i]-arr[i-1]; //买入就有卖出,所以卖出当天不能买入,指针再加1 i++; } } return profit; }
有些问题贪心算法只能找到局部最优解,并无法找到全局最优解
,好比那个股票问题,假如价格是[1,2,3,4,5]