Greedy Algorithm Summary and Leetcode Topic N

1 Why am I writing this summary

1.1 Byte written test questions

Xiao Ming is playing a clearance game, the initial blood volume is 1, there are monsters or blood packs in the level (a positive number means the amount of blood that can be recovered from the blood pack, and a negative number means the damage value of the monster), when the blood pack is picked up, the blood volume will be increased , when encountering a monster, it will lose blood. Now the initial blood volume is specified as x, and the level is an array. Xiao Ming must play the game in the order of the array. When he encounters a monster, he can choose to throw this monster to the end of the array. Xiao Ming Monsters can be moved to the end of the array infinitely. Ask Xiaoming how many times he can move to survive. If he can't survive no matter how many times he moves, then return -1. Assuming the level is like this [-200,-300,400], then return -1 , if it is like this [200,100,-250,-60,-70,100], then return 1, just move -250 to the end,

Idea: When you find that your blood volume is insufficient, choose the level that consumes the most blood volume from all the levels you have traversed so far and put it in the last level. If you remove the level that consumes the most blood volume If the blood volume is still negative, it will directly return -1, indicating that the level cannot be cleared

2 Summary

2.1 What is a local optimum?

Greedy focuses on the local optimum, where the current optimum (referring to the optimal solution among all the elements currently traversed) is also a kind of local optimum, and the general most solution involves the maximum value of the data, and as As the elements continue to expand to the right, the optimal value may be constantly changing, so generally the heap can be used to dynamically maintain its optimal value .

2.2 Solving some classifications of greedy problems

Divided into types such as opposite double pointers, same direction double pointers and most value heaps

2.2.1 Differences in problem-solving ideas between facing double pointers and same-directing double pointers

  • Opposite double pointers are usually used to deal with problems in sorted arrays, such as finding the sum of two numbers equal to a specific value (such as LeetCode topic 167. The sum of two numbers II - input sorted array), or reversing an array (such as LeetCode topic 344. Reverse a string). For this type of problem, two pointers start from the head and tail of the array respectively, and move to the middle according to the size relationship of the pointed elements until the two pointers meet.

  • Co-directional double pointers are usually used to deal with problems that require continuous elements in arrays or strings to meet specific conditions, such as finding the smallest subarray that satisfies a specific sum (such as LeetCode topic 209. The smallest subarray), or finding the longest The substring that satisfies certain conditions (such as LeetCode topic 424. The longest repeated character after replacement). For this type of problem, one pointer (fast pointer) is used to traverse the array or string, and the other pointer (slow pointer) is used to maintain the range that satisfies the condition.

2.2.2 On LeetCode, there are many topics related to the same direction double pointer and greedy strategy. Give two examples:

  • Topic 209. Minimal-length subarray: Find the smallest-length contiguous subarray in an array of positive integers whose sum is at least a certain value. The way to solve this problem is to maintain a sliding window, the left and right boundaries of the window are double pointers in the same direction. Move the right pointer to the right to increase the sum of the subarrays, and when the sum of the subarrays reaches or exceeds the target value, try to move the left pointer to the right to decrease the length of the subarray.

  • Topic 424. Longest Repeated Characters After Substitution: Given a string and an integer k, find the longest substring of repeated characters that can be obtained by substituting at most k characters. The solution to this problem is also to maintain a sliding window, and the left and right boundaries of the window are double pointers in the same direction. The right pointer moves to the right to increase the length of the substring, and when the maximum number of characters in the substring plus k is less than the length of the substring, the left pointer moves to the right.

2.3 Some topics that need preprocessing

2.3.1 Sorting may be required

  1. handing out cookies

2.3.2 It may be necessary to hashMap to count the last occurrence of characters

  1. Divide letter intervals

3 Greedy strategy for problem solving using the heap

3.1 leetcode1046. The weight of the last stone

3.2 leetcode 1642. The furthest building that can be reached

leetcode 1642. The furthest building that can be reached

standard answer:

   // 砖块优先,砖块数量不足时考虑将当前替代最小高度的梯子改用砖块替代
    public int furthestBuilding(int[] heights, int bricks, int ladders) {
    
    
        int n=heights.length;
        //大根堆
        PriorityQueue<Integer>pq=new PriorityQueue<>((o1,o2)->(o2-o1));
        int i;
        int sum=0;//表示当前需要的梯子数量
        int sum_bri=0;
        for(i=1;i<n;i++){
    
    
            int diff=heights[i]-heights[i-1];
            if(diff>0){
    
    
                pq.offer(diff);
                sum_bri+=diff;//砖块的数量之和
                if(sum_bri>bricks){
    
    
                    sum_bri-=pq.poll();
                    sum++;
                }
                if(sum>ladders){
    
    
                    return i-1;
                }
            }
        }
        return n-1;
    }
	//每次先放梯子,当梯子数量不足时,将当前使用过的梯子中,替代阶梯数最少的梯子找出来用对应砖头替换,替换出来的梯子拿到当前使用
 public int furthestBuilding(int[] heights, int bricks, int ladders) {
    
    
        int n=heights.length;
        //int[]:
        PriorityQueue<Integer>pq=new PriorityQueue<>((o1,o2)->(o1-o2));
        int i;
        int sum=0;//表示当前需要的砖块数量
        for(i=1;i<n;i++){
    
    
            int diff=heights[i]-heights[i-1];
            if(diff>0){
    
    
                pq.offer(diff);
                if(pq.size()>ladders){
    
    
                    sum+=pq.poll();
                }
                if(sum>bricks){
    
    
                    return i-1;
                }
            }
        }
        return n-1;
    }
    ```
    我的答案:(有几个特别长的case过不了)
    
```java
public int furthestBuilding2(int[] heights, int bricks, int ladders) {
    
    
        int n=heights.length;
        PriorityQueue<Integer>pq=new PriorityQueue<>((o1,o2)->(o1-o2));
        int i;
        for(i=1;i<n;){
    
    
            int diff=heights[i-1]-heights[i];
            // System.out.println("i:"+i+",diff:"+diff+",heights[i-1]:"+heights[i-1]+",heights[i]:"+heights[i]);
            if(diff<0){
    
    
              int diffAbs=-diff;
                if(ladders>0){
    
    
                    ladders--;
                    pq.add(diffAbs);
                }else if(bricks>=diffAbs){
    
    
                    bricks-=diffAbs;
                }else{
    
    
                    int newd=Integer.MAX_VALUE;
                    if(!pq.isEmpty()){
    
    
                      newd=pq.peek();
                    }
                    if(bricks>=newd){
    
    
                        pq.poll();
                        bricks-=newd;
                        ladders++;
                        i=i-1;
                    }else{
    
    
                        break;
                    }
                }
            }
            i++;
        }
        return i-1;
    }

Here's another binary search method:
Every Tail understands:

3.3 121. The best time to buy and sell stocks (although the method of using the heap is not the optimal solution, it is convenient to compare with other topics to obtain the optimal solution)

3.3.1 What are the similarities and differences between this question and 3.1 and 3.2?

The same: both involve taking the maximum value, and the maximum value may change.
Different: the maximum value in the questions in 3.1 and 3.2 can only be used once, and once used, poll operation is required, while the maximum value in this question can be reused. Formally this feature leads to the fact that the former can only use the heap to maintain the maximum value (because it may be necessary to obtain the maximum value multiple times, even if multiple maximum values ​​are used, the poll operation can only be maintained by the heap), although this question also has the maximum value many times, But the most value can be reused, there is no poll operation.

class Solution {
    
    
	// 使用堆维护最小值
   	public int maxProfit(int[] prices) {
    
    
        int n=prices.length;
        //表示到第i天时,股票的历史最低点价格
        PriorityQueue<Integer>pq=new PriorityQueue<>((o1,o2)->(o1-o2));
        
        int max=0;
        for(int i=0;i<n;i++){
    
    
            pq.add(prices[i]);
            max=Math.max(max,prices[i]-pq.peek());
        }
        return max;
    }

    // 方法:使用dp维护已经遍历值得最小值
    public int maxProfit3(int[] prices) {
    
    
        int n=prices.length;
        //表示到第i天时,股票的历史最低点价格
        int[]f=new int[n];
        f[0]=prices[0];
        int max=0;
        for(int i=1;i<n;i++){
    
    
            f[i]=Math.min(f[i-1],prices[i]);
            max=Math.max(max,prices[i]-f[i]);
        }
        return max;
    }
	// 方法:使用一个变量维护已经遍历值得最小值
    public int maxProfit(int[] prices) {
    
    
        int n=prices.length;
        //表示到第i天时,股票的历史最低点价格
        int min=prices[0];

        int max=0;
        for(int i=1;i<n;i++){
    
    
            min=Math.min(min,prices[i]);
            max=Math.max(max,prices[i]-min);
        }
        return max;
    }
    
    // 使用单调栈维护已经遍历序列的最小值
    public int maxProfit2(int[] prices) {
    
    
        int n=prices.length;

        Deque<Integer>st=new ArrayDeque<>();
        int max=0;
        st.add(prices[0]);
        for(int i=1;i<n;i++){
    
    
            int p=prices[i];
            if(!st.isEmpty()&&p<=st.peekLast()){
    
    
                max=Math.max(st.peekLast()-st.peekFirst(),max);
                while(!st.isEmpty()&&st.peekLast()>=p){
    
    
                    st.pollLast();
                }
            }
            st.addLast(p);

        }
        if(!st.isEmpty()){
    
    
            max=Math.max(st.peekLast()-st.peekFirst(),max);
            
        }
        return max;
    }
}

3.3 The minimum refueling times of 871 questions and

3.4 630 Curriculum Schedule III

4 Greedy strategy for problem solving using opposite double pointers

4.1 167. Sum of Two Numbers II - Input sorted array

Idea: double pointer to solve the deviation

class Solution {
    
    
    public int[] twoSum(int[] numbers, int target) {
    
    
        int n=numbers.length;
        int i=0,j=n-1;
        while(i<j){
    
    
            int sum=numbers[i]+numbers[j];
            if(sum>target){
    
    
                j--;
            }else if(sum<target){
    
    
                i++;
            }else{
    
    
                return new int[]{
    
    i+1,j+1};
            }
        }
        return new int[]{
    
    i+1,j+1};

    }
    
}

4.2 11. The container with the most water

Idea: The water that the container can hold depends on the short board. Keep moving the pointer that represents the smaller value among the two pointers until the two pointers meet, and take the maximum value among all the possible water holding capacity obtained.

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

5 Same Direction Double Pointer and Greedy Strategy

5.1 763. Divide the letter interval (find the subsequence in the array that meets certain conditions)

class Solution {
    
    
    public List<Integer> partitionLabels(String s) {
    
    
        
        int n=s.length();

        int[]mp=new int[26];
        for(int i=0;i<n;i++){
    
    
            mp[s.charAt(i)-'a']=i;
        }
        List<Integer>res=new ArrayList<>();
        
        int start=0,end=0;
        for(int i=0;i<n;i++){
    
    
            end=Math.max(end,mp[s.charAt(i)-'a']);
            if(end==i){
    
    
                res.add(i-start+1);
                start=i+1;
            }
        }
        return res;
    }
}

Guess you like

Origin blog.csdn.net/yxg520s/article/details/131989933