LEETCODETOP之贪心算法(盛最多水容器,跳跃游戏,根据身高重建队列,买卖股票最佳时机等等)

76 阅读3分钟

image.png 贪心思想: 如果每条垂线一样高,那么取两边最大,但是很显然这不是最优解,但是却可以先从两边开始。通过双指针合围,思路是先用0,length-1从外围向里移动,我们可以认为移动谁取决于height[i],height[j]谁的值小是最优算法。至于为什么(同时也是为什么不用暴力枚举的方法),看下图

image.png

i=0,j=8时,面积为8,而如果是向左移动j指针,那么面积永远也不可能大于8,相当于遍历中我们可以直接排除i=0时,j从1~7。 贴代码:

class Solution {
    public int maxArea(int[] height) {
    	if (height.length < 2) return 0;
    	int i=0,j = height.length-1;
    	int maxArea = Math.min(height[i], height[j])*(j-i);
    	while(i!=j) {
    		 int area = Math.min(height[i], height[j])*(j-i);
    		 maxArea = maxArea > area ? maxArea:area;
    		 if (height[i] > height[j]) {
    			 j--;
    		 } else {
    			 i++;
    		 }
    	}
    	return maxArea;
    }
}

关键词:双指针

image.png 贪心思想:??? 解法:遍历当前数组,通过i+nums[i]维护当前能到的最大值, 并比较最大值与与当前下标与最大下标的关系即可 代码如下:

class Solution {
    public boolean canJump(int[] nums) {
    	int maxIndex = 0;
    	for (int i = 0; i < nums.length; i++) {
    		if (i>maxIndex) return false;
    		maxIndex = Math.max(maxIndex, i+nums[i]);
    		if (maxIndex>=nums.length-1) {
    			return true;
    		}
		}
    	return false;
    }
}

image.png

贪心思想:通过倒推,如果跳跃一次来说,局部最优我们可以认为是能一次到最后一个位置y离最远的下标x,那通过这种局部最优能推导全局最优吗?是可以的!我们只要找到能一次到x的离最远下标直到下标为0即可。 有没有可能不是最优解,不会。证明:对于x到y的下标,可以知道最快也需要跳跃一次,而x满足仅一次,其他未必;而对于任何n次能到达x到y的下标,也一定能n次到达x。完美的贪心算法题(如***++6++,1,1,1,++3++***,1,1,1)

class Solution {
    public static int jump(int[] nums) {
    	int index = nums.length-1;
    	int step=0;
    	while(index>0) {
    		for (int i = 0; i < index; i++) {
				if (i+nums[i] >= index) {
					index = i;
					step++;
					break;
				}
			}
    	}
    	return step;

    }
}

image.png 题意:就是题目给出的【身高,有几个身高大于等于他排他前面】的数组是打乱的需要重新排下序,注意:数组是打乱的但是数据是正确的 贪心思想:如果大家身高都不一样,是不是从高往低排就可以了?如果大家升高都一样,是不是按照people[x][1]的顺序从低往高排就可以了?是的。可以通过组合推导最优解吗,不行但是可以帮助减少逻辑处理。通过这种排序后遍历添加至新数组,这个过程中people[x][1]就是它所在数组的位置。 证明:因为已经对身高做过排序,people[x][1]所在的位置比他高的已经排过了,后面没有比他高的了,而当比他矮的插在他前面时并不影响people[x][1]的正确性,不过需要注意下数组的移动 小技巧:一般这种数对,还涉及排序的,根据第一个元素正向排序,根据第二个元素反向排序,或者根据第一个元素反向排序,根据第二个元素正向排序,往往能够简化解题过程。

class Solution {
	public int[][] reconstructQueue(int[][] people) {
		Arrays.sort(people, (a, b) -> {
			if (a[0] == b[0]) {
				return a[1] - b[1];
			} else {
				return b[0] - a[0];
			}
		});
		int[][] res = new int[people.length][2];
		for (int i = 0; i < people.length; i++) {
			if (people[i][1]>=i) {
				res[i] = people[i];
			} else {
                int index = people[i][1];
				for (int j = i; j > index; j--) {
					res[j] = res[j-1];
				}
				res[index] = people[i];
			}
		}
		return res;

	}

}

image.png 思想:每次都取获取剩余没做过的最多的任务

class Solution {
	public static int leastInterval(char[] tasks, int n) {
		if (n == 0)
			return tasks.length;
		Map<String, Integer> map = new HashMap<>();
		int res = 0;
		for (int i = 0; i < tasks.length; i++) {
			if (map.containsKey(tasks[i] + "")) {
				int num = map.get(tasks[i] + "");
				map.put(tasks[i] + "", ++num);
			} else
				map.put(tasks[i] + "", 1);
		}
		String once = "";

		while (!map.isEmpty()) {

			String key = getNeIndex(map, once);
			once += key;
			res++;
			if (once.length() == n + 1) {
				once = "";
			}
		}
		return res;

	}
    // 每次都取获取剩余没做过的最多的任务
	public static String getNeIndex(Map<String, Integer> map, String once) {
		int max = -1;
		String key = "-";

		for (String k : map.keySet()) {
			if (!once.contains(k) && max < map.get(k)) {
				max = map.get(k);
				key = k;
			}
		}
		if (max != -1) {
			max--;
			if (max == 0)
				map.remove(key);
			else
				map.put(key, max);
		}
		return key;
	}
}

image.png 贪心思想:如果只有一次上涨,那么这次上涨就是最大利润。实际找出所有的上涨区间就可以得出最优解。证明:如果某次上涨区间中存在一次下跌,那么当我们避免这次下跌便可以得到更高的利润,所以只需要计算所有连续上涨的区间就行

class Solution {
    public int maxProfit(int[] prices) {
    	int res = 0;
    	for (int i = 0; i < prices.length-1; i++) {
    		int index = i+1;
    		if (prices[index]>prices[i]) {
    			res+=prices[index]-prices[i];
    			
    		}
			
		}
    	return res;
    }
}