代码随想录——贪心

题目来自《代码随想录》

文章目录

    • 455. 分发饼干
    • 376. 摆动序列
    • 53. 最大子数组和
    • 122. 买卖股票的最佳时机 II
    • 55. 跳跃游戏
    • 45. 跳跃游戏 II
    • 1005. K 次取反后最大化的数组和
    • 134. 加油站
    • 135. 分发糖果
    • 860. 柠檬水找零
    • 406. 根据身高重建队列
    • 452. 用最少数量的箭引爆气球
    • 435. 无重叠区间
    • 763. 划分字母区间
    • 56. 合并区间
    • 738. 单调递增的数字
    • 714. 买卖股票的最佳时机含手续费
    • 968. 监控二叉树

455. 分发饼干

https://leetcode-cn.com/problems/assign-cookies/

	/**
	 * 执行用时:7 ms, 在所有 Java 提交中击败了99.93%的用户
	 * 内存消耗:42.5 MB, 在所有 Java 提交中击败了12.66%的用户
	 * @param g 孩子胃口值
	 * @param s 饼干值
	 * @return 能够满足多少个孩子的胃口
	 */
	public int findContentChildren2(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int start = 0;
        int count = 0;
        for (int i = 0; i < s.length && start < g.length; i++) {
            if (s[i] >= g[start]) {
                start++;
                count++;
            }
        }
        return count;
    }
	
	/**
	 * 1. 平什么版
	 * 执行用时:8 ms, 在所有 Java 提交中击败了23.59%的用户
	 * 内存消耗:42.1 MB, 在所有 Java 提交中击败了63.17%的用户
	 * @param g 孩子胃口值
	 * @param s 饼干值
	 * @return 能够满足多少个孩子的胃口
	 */
	public int findContentChildren(int[] g, int[] s) {
		int ans = 0;
		Arrays.sort(g);
		Arrays.sort(s);
		int ind = 0;
		System.out.println(s.length);
		for(int i=0; i<g.length; i++) {
			System.out.println("i=" + i + ",ind:" + ind);
			while(ind < s.length) {
				System.out.println("第" + i + "个孩子胃口:" + g[i] + ",第" + ind + "个饼干:" + s[ind]);
				if(g[i] <= s[ind]) {
					System.out.println("满足胃口" );
					ans++;
					ind++;
					System.out.println("满足后ind变为" + ind );
					break;
				} else {
					ind++;
					System.out.println("不满足后ind变为" + ind );
				}
			}
			if(ind == s.length) break;
		}		
		return ans;
	}

376. 摆动序列

https://leetcode-cn.com/problems/wiggle-subsequence/

	/*
	 * 碰到【0,0,0】输出就是2,不对
	 */
	public int wiggleMaxLength(int[] nums) {
		if(nums.length <=2) {
			if( nums.length == 2 && nums[0] == nums[1]) return 1;
			return nums.length;
		}
		int ans = 2;
		int pre = 0; //前一对差
		int next = 0; //下一个差
		for(int i=1; i<nums.length-1; i++) {//判断从第二个到倒数第二个
			pre = nums[i-1] - nums[i];
			next = nums[i] - nums[i+1];
			if((pre > 0 && next < 0) || (pre < 0 && next >0)) ans++;
		}
		return ans;
	}
	
	/*
	 * 正确答案
	 */
	public int wiggleMaxLength2(int[] nums) {
		if(nums.length <=1) return nums.length;
		int up = 1;
		int down = 1;
		for(int i=1; i<nums.length; i++) {
			if(nums[i]-nums[i-1] > 0) up = down+1;
			if(nums[i]-nums[i-1] < 0) down = up+1;
		}		
		return Math.max(up, down);
	}

53. 最大子数组和

https://leetcode-cn.com/problems/maximum-subarray/

	//当前连续和为负数时,立马放弃
	public int maxSubArray(int[] nums) {
		int ans = Integer.MIN_VALUE;
		int curans = 0;
		for(int i=0; i<nums.length; i++) {
			curans += nums[i];
			if(curans > ans) ans = curans;
			if(curans < 0) curans = 0;
		}		
		return ans;
	}

122. 买卖股票的最佳时机 II

一遍过,蔑视中等题

	/*
	 * 执行用时:1 ms, 在所有 Java 提交中击败了87.62%的用户
	 * 内存消耗:41.5 MB, 在所有 Java 提交中击败了11.16%的用户
	 */
	public int maxProfit(int[] prices) {
		int ans = 0;
		for(int i=1; i<prices.length; i++) {
			if(prices[i]-prices[i-1] > 0) ans+=prices[i]-prices[i-1];
		}		
		return ans;	
	}

55. 跳跃游戏

https://leetcode-cn.com/problems/jump-game/

  1. 递归思路
  2. 找能覆盖的最大面积
	public boolean canJump(int[] nums) {
		if(nums.length <= 1) return true;
		return digui(nums, nums.length-1);
	}

	public boolean digui(int[] nums, int s) {
		if(nums[0] >= s) {
			System.out.println("从第" + s + "格可以跳到起点");
			return true;
		}
		
		for(int i=s-1; i>=0; i--) {
			if(nums[i] >= s-i) {
				System.out.println("从第" + s + "格跳往第" + i + "格");
				return digui(nums, i);
			}
		}
		return false;
	}

45. 跳跃游戏 II

	/*
	 * 1. 从后往前遍历所有,这种递归会超时的
	 */
	LinkedList<Integer> path = new LinkedList<>();	
	
	int ans = Integer.MAX_VALUE;
	public int jump(int[] nums) {
		if(nums.length == 1) return 0;
		digui(nums, nums.length-1, 0);
		return ans;
    }
	
	public void digui(int[] nums, int s, int curstep) {
		if(nums[0] >= s) {
			curstep++;
			System.out.println("从第" + s + "格可以跳到起点,存入step为:" + curstep);
			if(curstep < ans) ans = curstep;
			path.add(curstep);
		}
		
		for(int i=s-1; i>=0; i--) {
			if(nums[i] >= s-i) {
				System.out.println("从第" + s + "格跳往第" + i + "格");
				digui(nums, i, curstep+1);
			}
		}
	}
	
	/*
	 * 2. 还是从后往前,不递归,每次只迈最大步。但这玩意效率也不高
	 * 执行用时:14 ms, 在所有 Java 提交中击败了24.58%的用户
	 * 内存消耗:42 MB, 在所有 Java 提交中击败了13.65%的用户
	 */

	public int jump2(int[] nums) {
		int ind = nums.length-1;
		int step = 0;
		while( ind > 0) {
			for(int i=0; i<ind; i++) {
				if(i+nums[i] >= ind) {
					ind = i;
					step++;
					break;
				}
			}
		}
		return step;
	}
	
	/*
	 * 3. 没有达到我上一次跳跃的最远距离,就不跳
	 * 这过程中储存下一次能跳的最远距离
	 * 达到我上一次跳的最远距离了,就跳,次数加一
	 * 执行用时:2 ms, 在所有 Java 提交中击败了44.78%的用户
	 * 内存消耗:42.1 MB, 在所有 Java 提交中击败了11.47%的用户
	 */
	public int jump3(int[] nums) {
		int res = 0;
		int cur = 0;//当前最远
		int next = 0; //下一步最远
		for (int i = 0; i <= cur && cur < nums.length - 1; ++i) {//在条约范围内
			next = Math.max(next, nums[i] + i);//这比的不是cur,比的是next
			if(i == cur) {
				cur = next;
				res++;
			}			
		}	
		return res;
	}

1005. K 次取反后最大化的数组和

	public int largestSumAfterKNegations(int[] nums, int k) {
        // 排序,把可能有的负数排到前面
        Arrays.sort(nums);
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            // 贪心:如果是负数,而k还有盈余,就把负数反过来
            if (nums[i] < 0 && k > 0) {
                nums[i] = -1 * nums[i];
                k--;
            }
            sum += nums[i];
        }
        Arrays.sort(nums);
        // 1. 如果k没剩,那说明能转的负数都转正了,已经是最大和,返回sum
        // 2. 如果k还剩偶数个就自己抵消掉,不用删减,
        // 3. 如果k还剩奇数个就减掉2倍最小正数。
        if( k%2 != 0) sum -= 2*nums[0];
        
        return sum;
	}

134. 加油站

https://leetcode-cn.com/problems/gas-station/

	/*
	 * 方法1:
	 * (1)如果gas和小于cost和,那就到不了(2)起点在gas和cost差最大的地方
	 * 答案错误!
	 * 如:gas = [5,8,2,8], cost = [6,5,6,6]
	 */
	public int canCompleteCircuit(int[] gas, int[] cost) {
		int gassum = 0; // 能提供的汽油总和
		int costsum = 0; // 消耗的汽油总和
		int ans_index = 0; 
		int ans = 0;
		for(int i=0; i<gas.length; i++) {
			gassum += gas[i];
			costsum += cost[i];
			if( (gas[i]-cost[i]) > ans ) {
				ans = gas[i]-cost[i];
				ans_index = i;
			}
		}
		if( gassum < costsum ) ans_index = -1;
		return ans_index;
	}
	
	/*
	 * 方法2:
	 * 一直计算gas[i]-cost[i]的累加和,出现负数了,累加和置0,从下一位开始新的计算和记录
	 */
	public int canCompleteCircuit2(int[] gas, int[] cost) {
		int index = 0; //最终答案
		int tolsum = 0; //gas和-cost和
		int cursum = 0; //gas[i]-cost[i]的累加和,出现负数了,累加和置0
		
		for(int i=0; i<gas.length; i++) {
			tolsum += gas[i]-cost[i];
			cursum += gas[i]-cost[i];
			if( cursum < 0 ) {
				cursum = 0;
				index = i+1;
			}			
		}
		if( tolsum < 0) return -1;
		return index;
	}

135. 分发糖果

https://leetcode-cn.com/problems/candy/

	/*
	 * 一次是从左到右遍历,只比较右边孩子评分比左边大的情况。
	 * 一次是从右到左遍历,只比较左边孩子评分比右边大的情况。
	 */
	public int candy(int[] ratings) {
		int[] candy = new int[ratings.length];
		//1. 从左到右。右边比左边大,右边就加一。
		candy[0] = 1;
		for(int i=1; i<ratings.length; i++) {
			candy[i] = 1;
			if( ratings[i] > ratings[i-1]) {
				System.out.println(i + "位置上的值比" + (i-1) + "位置上的大");
				
				candy[i] = candy[i-1] + 1;
				System.out.println(i + "位置上的值加完1为" + candy[i]);
			}
		}		
		//2. 从右到左。左边比右边大,左边就加一
		//因为从左到右加过一次!所以要判断是从左边过来的大还是右边加一之后大!!
		for(int i=ratings.length-2; i>=0; i--) {
			if( ratings[i] > ratings[i+1]) {
				candy[i] = Math.max(candy[i], candy[i+1] + 1);
			}
		}		
		//3. 求和
		int ans = 0;
		for( int i: candy) {
			System.out.print(i + " ");
			ans += i;
		}
		return ans;
	}

860. 柠檬水找零

https://leetcode-cn.com/problems/lemonade-change/
一遍过

		/*
		 * 1. 10块的只能用5块的找
		 * 2. 20的:①10+5 ②5+5+5
		 * 注意这题是有顺序的,
		 */
	
	public boolean lemonadeChange(int[] bills) {
		int num5 = 0;
		int num10 = 0;
		int num20 = 0;		
		for(int i=0; i<bills.length; i++) {
			if( bills[i] == 5) num5++;
			if( bills[i] == 10) {
				if( num5 > 0) {//5块的够找10块的
					num5--;
					num10++;
				}else return false; //不够找,false
			}
			if( bills[i] == 20) {
				if( num10 > 0 && num5 > 0) {//1. 优先10+5的找
					num5--;
					num10--;
					num20++;
				}else if(num5 >= 3) {//2. 5块的有三张够找
					num5 -= 3;
					num20++;
				}else return false; // 都不够找,false
			}
		}	
		return true;
	}

406. 根据身高重建队列

https://leetcode-cn.com/problems/queue-reconstruction-by-height/

  1. 数组的sort
  2. 多维数组遍历
  3. 多维数组List之间转化
	public int[][] reconstructQueue(int[][] people) {
		// 身高从大到小排(身高相同k小的站前面)
        Arrays.sort(people, (a, b) -> {
            if (a[0] == b[0]) return a[1] - b[1];
            return b[0] - a[0];
        });
        int len = people.length;
        List<int[]> res= new ArrayList<>();
        for(int i = 0;i<len;i++){
            if (res.size() > people[i][1]){
                //结果集中元素个数大于第i个人前面应有的人数时,将第i个人插入到结果集的 people[i][1]位置
                res.add(people[i][1],people[i]);
            }else{
                //结果集中元素个数小于等于第i个人前面应有的人数时,将第i个人追加到结果集的后面
                res.add(res.size(),people[i]);
            }
        }
        return res.toArray(new int[len][2]);
	}

452. 用最少数量的箭引爆气球

https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/

	//先排序,每有左边界超过上一个右边界的,答案加一
	public int findMinArrowShots(int[][] points) {
		if(points.length == 0) return 0;
        Arrays.sort(points, (p1, p2) -> p1[1] < p2[1] ? -1 : 1);
        int res = 1;
        int pre = points[0][1];
        for(int i=1; i<points.length; i++) {
        	if(points[i][0] > pre) {
        		res++;
        		pre = points[i][1];
        	}
        }   
        return res;
	}

435. 无重叠区间

https://leetcode-cn.com/problems/non-overlapping-intervals/

	//按右闭区间从小到大排序
	public int eraseOverlapIntervals(int[][] intervals) {
		if(intervals.length == 0) return 0;
		
		//得右边靠齐
        Arrays.sort(intervals, new Comparator<int[]>() {
            public int compare(int[] interval1, int[] interval2) {
                return interval1[1] - interval2[1];
            }
        });
        show(intervals);
        int res = 0;
        int pre = intervals[intervals.length-1][0];//最后一个的左区间
        System.out.println("最后一个区间的左为:" + pre);
        for(int i=intervals.length-2; i>=0; i--) {
        	if(intervals[i][1] > pre) {//当前右大于上一个左,重叠
        		System.out.println("倒数第" + (i+1) + "个区间的右后一个的左,即" 
        						+ intervals[i][1] + "大于" + pre);
        		res++;//答案加一,pre不变
        	}else{//当前左小于等于上一个左,没重叠
        		pre = intervals[i][0];//pre换人
        	}
        }   
        return res;
	}
	
	public void show(int[][] nums) {
		for(int i=0; i<nums.length; i++) {
			System.out.print("[");
			for(int j=0; j<nums[i].length; j++) {
				System.out.print(nums[i][j] + " ");
			}
			System.out.print("],");
		}
		System.out.print("\n");
	}
	
	//正解
    public int eraseOverlapIntervals2(int[][] intervals) {
        Arrays.sort(intervals, (a, b) -> {
            if (a[0] == a[0]) return a[1] - b[1];
            return a[0] - b[0];
        });

        int count = 0;
        int edge = Integer.MIN_VALUE;
        for (int i = 0; i < intervals.length; i++) {
            if (edge <= intervals[i][0]) {
                edge = intervals[i][1];
            } else {
                count++;
            }
        }

        return count;
    }

763. 划分字母区间

https://leetcode-cn.com/problems/partition-labels/

	public List<Integer> partitionLabels(String s) {
        List<Integer> list = new LinkedList<>(); //储存答案
        int[] edge = new int[26];
        char[] chars = s.toCharArray(); //统计每个字符出现的最后位置存入edge中
        for (int i = 0; i < chars.length; i++) {
            edge[chars[i] - 'a'] = i;
        }
        int idx = 0;//当前字符最远边界
        int last = -1;//已经分割出去的片段总长度片段长度
        for (int i = 0; i < chars.length; i++) {
        	//idx表示当前片段最小边界 = max(上个字符最远位置,当前字符最远位置)
            idx = Math.max(idx,edge[chars[i] - 'a']);
            if (i == idx) {//!!!如果当前字符最远位置和当前字符位置一样,得到答案
                list.add(i - last);
                last = i;
            }
        }
        return list;		
	}

56. 合并区间

https://leetcode-cn.com/problems/merge-intervals/
注意考虑最后一对有没有存进去

	public int[][] merge(int[][] intervals) {
		List<int[]> res = new LinkedList<>();
		Arrays.sort(intervals, (o1, o2) -> Integer.compare(o1[0], o2[0]));//按左边界从小到大排      
        show(intervals);
        //每有一个左小于上一个右,合并
        int l = intervals[0][0];//第一个左边界
        int r = intervals[0][1];//第一个右边界
        for(int i=1; i<intervals.length; i++) {
        	if(intervals[i][0] <= r ) {//如果左小于上一个右,l不变,r扩大
        		r = Math.max(intervals[i][1], r);//当前右和上一个右当中大的当新右
        	}else{//如果左大于上一个右,res.add,更新l r
        		res.add(new int[]{l, r});
                l = intervals[i][0];//更新左边界
                r = intervals[i][1];//更新右边界       		
        	}
        }
        res.add(new int[]{l, r});//最后一对没加,加一下
        return res.toArray(new int[res.size()][]);
	}

738. 单调递增的数字

https://leetcode-cn.com/problems/monotone-increasing-digits/

	/*
	 * 局部最优:遇到strNum[i - 1] > strNum[i]的情况,
	 * 让strNum[i - 1]--,然后strNum[i]给为9,可以保证这两位变成最大单调递增整数。
	 * 全局最优:得到小于等于N的最大单调递增的整数。
	 * 
	 */
	public int monotoneIncreasingDigits(int n) {
		if (n==0)return 0;
        char[] chars= Integer.toString(n).toCharArray();
        //start记录从第几位开始后面都是9了。一开始设置最大值是为了防止本身就是递增的
        int start=Integer.MAX_VALUE;
        for (int i=chars.length-1;i>0;i--){
            if (chars[i]<chars[i-1]){
                chars[i-1]--;
                start=i;
            }
        }
        StringBuilder res=new StringBuilder();
        for (int i=0;i<chars.length;i++){
            if (chars[i]=='0'&&i==0)continue;//防止出现09这样的情况
            if (i>=start){//到了start的位置,后面全都搞成9
                res.append('9');
            }else res.append(chars[i]);//在start之前该是啥是啥
        }
        return Integer.parseInt(res.toString());
	}

714. 买卖股票的最佳时机含手续费

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/

	public int maxProfit(int[] prices, int fee) {
		int sum = 0;
		int buy = prices[0] + fee;
		for(int i=0; i<prices.length; i++) {
			if( prices[i] + fee < buy) {//有更低的买入价,更新
				buy = prices[i] + fee;
			} else if(prices[i] > buy) {//如果我现在已经能赚钱了
				sum += prices[i] - buy; //先把肯定能赚到的这一部分加上
				buy = prices[i]; 
				//这样接下来有两种情况
				//1. 如果后面的价格比我卖出的buy高,我依然可以赚到这部分钱
				//2. 如果后面的价格低了,有更低的价格买入价会更新的,我们赚了就跑了,完美
			}
		}
		return sum;
	}

968. 监控二叉树

https://leetcode-cn.com/problems/binary-tree-cameras/

	/*
	 * 2. 该节点无覆盖0(无摄像),本节点有摄像头1,本节点有覆盖2(无摄像)。空结点当做有覆盖
	 * 后序遍历,从下往上,从叶子节点的爹开始安装摄像头
	 */
	
	public int count = 0;//记录摄像头个数
	public int minCameraCover(TreeNode root) {
		if( trval(root) == 0) count++; //如果答案是0,说明就一个根,答案++
		return count;
	}
	private int trval(TreeNode root) {
		// 1. 空节点,该节点有覆盖,返回2
		if (root == null) {
			System.out.println("当前结点为空,返回2");
			return 2;
		}
		//后序遍历树
		int l = trval(root.left);
		int r = trval(root.right);		
		// 2. 左右节点都有覆盖无摄像,那当前点是无覆盖无摄像的
		if (l == 2 && r == 2) {
			System.out.println("当前结点" + root.val + "的左右孩子都为空,返回0");
			return 0;
		}
		// 4. 左右有一个孩子没有覆盖,那就该装个摄像头了
		// 注意第一次错在这了!!!左右有一个是0的优先级很高!!先判断!!!
		if( l == 0 || r == 0) {
			System.out.println("当前结点" + root.val + "的左右孩子有一个是0,返回1");
			count++;
			return 1;
		}
		// 3. 左右有一个有摄像,不用放摄像了,本结点被覆盖了
		if( l == 1 || r == 1) {
			System.out.println("当前结点" + root.val + "的左右孩子有一个是1,返回2");
			return 2;
		}
		
		return -1;//走不到这的
	}

你可能感兴趣的:(数据结构与算法,java)