Leetcode notes -- Introduction to greedy algorithm

Posted by hopelessX on Thu, 24 Feb 2022 13:02:34 +0100

Catalogue of series articles

I Array type problem solving method 1: dichotomy
II Array type problem solving method 2: Double finger needle method
III Array type problem solving method 3: sliding window
IV Array type problem solving method 4: simulation
V The basic operation and classic topics of the linked list
Vi Classic title of hash table
VII Classic title of string
VIII KMP of string
IX Solution: double pointer
X Classic title of stack and queue
Xi top-K problem in stack and queue
XII The first, middle and last order traversal of binary tree
XIII Sequence traversal of binary tree and related topics
XIV Binary tree part of binary tree attributes related topics
XV Modification and construction of binary tree in binary tree chapter
XVI Properties of binary search tree
XVII The problem of common ancestor in binary tree discourse
XVIII Modification and construction of binary search tree in binary tree chapter
XIX Combinatorial problem of backtracking algorithm
XX Segmentation, subset and total arrangement of backtracking algorithm
Updating

preface

The question brushing route comes from: Code Capriccio
Greedy algorithm: the local optimal solution is stacked into the global optimal solution. There is no fixed routine. It is common sense derivation and counter examples.

Inscription

1, Simple topic

455. Distribution of biscuits

Leetcode link
Suppose you are a great parent and want to give your children some cookies. However, each child can only give one biscuit at most.
For each child I, there is an appetite value g[i], which is the minimum size of biscuits that can satisfy the children's appetite; And every cookie j has a size s[j]. If s[j] > = g[i], we can assign this biscuit j to child I, and the child will be satisfied. Your goal is to meet as many children as possible and output this maximum value.


Solution:

class Solution {
    public int findContentChildren(int[] g, int[] s) {
    	// Sort first
        Arrays.sort(g);
        Arrays.sort(s);
        int i = 0; // Child subscript
        int j = 0; // Appetite subscript
        // And cookies and children
        while (i < g.length && j < s.length) {
            if (s[j] >= g[i]) {
            	// Biscuits satisfy a child's appetite
                i++;
                j++;
            } else {
            	// The child has too much appetite and looks back for big biscuits
                j++;
            }
        }
        return i;
    }
}

1005. Maximum array sum after K negations

Leetcode link
Give you an integer array nums and an integer k. modify the array as follows:
Select a subscript i and replace num [i] with - num [i].
Repeat this process exactly k times. You can select the same subscript i multiple times. After modifying the array in this way, the maximum possible sum of the array is returned

Solution:
Invert the smallest number every time

class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        for (int i = 0; i < k; i++) {
        	// Sort, reverse
            Arrays.sort(nums);
            nums[0] = -nums[0];
        }
        // Sum
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }
        return sum;
    }
}

Optimization:

class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
    	// Sort first
        Arrays.sort(nums);
        // Invert the negative number in the [0,k) range
        for (int i = 0; i < nums.length; i++) {
            if (k > 0 && nums[i] < 0) {
                nums[i] = -nums[i];
                k--;
            }        
        }
        // See if the remaining k is even or odd
        if (k % 2 == 1) {
        	// Odd number, invert the smallest number
            Arrays.sort(nums);
            nums[0] = -nums[0];
        }
        // Even number without operation
        // Sum
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }
        return sum;
    }
}

860. Lemonade change

Leetcode link
At the lemonade stand, each glass of lemonade costs $5. Customers line up to buy your products, one cup at a time (in the order bill is paid).
Each customer buys only one glass of lemonade and then pays you $5, $10 or $20. You must give each customer the correct change, which means that the net transaction is $5 per customer. Note that you don't have any change at first. Give you an integer array bills, where bills[i] is the bill paid by the ith customer. If you can give each customer the correct change, return true, otherwise return false.


Solution:
Record the number of chapters of five yuan and ten yuan, and change the money according to the situation. When changing the money, the code is concise, and it is not necessary to judge whether there is money in hand. After finding the money, it is judged according to the positive and negative number of money.

class Solution {
    public boolean lemonadeChange(int[] bills) {
        int five = 0;
        int ten = 0;
        for (int i = 0; i < bills.length; i++) {
        	// Case 1 Received 5 yuan
            if (bills[i] == 5) {
                five++;
            }
            // Case 2 Receive 10 yuan, change 5 quickly
            if (bills[i] == 10) {
                five--;
                ten++;                   
            }
            // Situation 3 Got 20, 10 fast, find the fast one first
            if (bills[i] == 20) {
                if (ten > 0) {
                    ten--;
                    five--;
                } else {
                    five -= 3;
                }
            }
            // judge
            if (five < 0 || ten < 0) {
                return false;
            }
        }
        return true;
    }
}

2, Medium topic

376. Swing sequence

Leetcode link
If the difference between consecutive numbers strictly alternates between positive and negative numbers, the number sequence is called swing sequence. The first difference (if any) may be positive or negative. A sequence with only one element or two unequal elements is also regarded as a swing sequence.
For example, [1, 7, 4, 9, 2, 5] is a swing sequence because the difference (6, - 3, 5, - 7, 3) is alternating positive and negative.
On the contrary, [1, 4, 7, 2, 5] and [1, 7, 4, 5, 5] are not swing sequences. The first sequence is because its first two differences are positive, and the second sequence is because its last difference is zero.
Subsequences can be obtained by deleting some (or no) elements from the original sequence, and the remaining elements maintain their original order.
Give you an integer array nums, which returns the length of the longest subsequence of the swing sequence in nums.

Solution:
Two consecutive pairs of differences are opposite, indicating swing

In special case, the longest swing subsequence of [2,2,5] is [2,5], but the difference is [0,5], so it is necessary to add equal to

class Solution {
    public int wiggleMaxLength(int[] nums) {
        if (nums.length <= 1) return nums.length;
        int preDiff = 0; // Last pair difference
        int curDiff = 0; // Current difference
        int count = 1; // record
        for (int i = 1; i < nums.length; i++) {
            curDiff = nums[i] - nums[i - 1];
            // The difference between two consecutive pairs is opposite. Note that it is equal to
            if ((curDiff > 0 && preDiff <= 0) || (curDiff < 0 && preDiff >= 0)) {
                count++;
                preDiff = curDiff;
            }
        }
        return count;
    }
}

738. Monotonically increasing numbers

Leetcode link
Solution:
Local optimization: when strNum[i - 1] > strNum[i], let strNum[i - 1] –, and then strNum[i] is 9
Traverse from the back to the front to find the position where you start to assign 9

class Solution {
    public int monotoneIncreasingDigits(int n) {
        if (n == 0) return 0;
        // Convert to char array
        char[] chars = String.valueOf(n).toCharArray();
        int start = 10;  // Maximum
        // Traverse from back to front
        for (int i = chars.length - 1; i > 0; i--) {
            if (chars[i] < chars[i - 1]) {
                chars[i - 1]--;
                start = i;
            }
        }
        // Splicing
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < chars.length; i++) {
        	// 10 - > 09, the first 0 is not spliced
            if (i == 0 && chars[i] == '0') continue;
            if (i >= start) {
                sb.append('9');
            } else {
                sb.append(chars[i]);
            }
        }
        return Integer.valueOf(sb.toString());
    }
}

3, Stock buying problem

122. The best time to buy and sell stocks II

Leetcode link
Give an array prices, where prices[i] represents the price of the stock on day I. On each day, you may decide to buy and / or sell shares. You can only hold at most one share at any time. You can also buy it and sell it on the same day. Return the maximum profit you can make.

Solution:
Local optimization: collect the positive profit every day. As long as the stock price is greater than that of the previous day, it will be counted as profit
Global Optimization: obtain the maximum profit.

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

714. The best time to buy and sell stocks includes handling fees

Leetcode link
Give an integer array prices, where prices[i] represents the stock price on day I; The integer fee represents the handling fee for trading stocks. You can complete transactions indefinitely, but you need to pay a handling fee for each transaction. If you have bought a stock, you can't continue to buy it until you sell it. Returns the maximum profit.

Solution:
The purchase price plus handling fee is the purchase price of the previous day.
Different from the above question, we can't calculate the daily profit without brains. For example: [1, 4, 8], when fee = 2, buy it at the price of 1 + 2 = 3. It can't be sold the next day, and then sell it again on the third three days with only one handling charge. How to calculate it? It can be assumed that 3 yuan is bought on the first day and 4 yuan is sold on the second day. The selling price is recorded. Because the price on the third day is greater than 4, it is calculated according to (8 - 4) on the third day, and the handling fee is less
When is the real sale? When [1, 6, 3, 8], 6 - (1 + 2) + 8 - (3 + 2) > 8 - (1 + 2). Because 3 + 2 < 6, the buying price on the third day is 5 yuan, which is less than the selling price of the previous day by 6 yuan.

class Solution {
    public int maxProfit(int[] prices, int fee) {
        int sum = 0;
        // Initialize to the buying price of the first day
        int buy = prices[0] + fee;
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] > buy) {
            	// If you can make a profit, calculate the profit, and record the price of this false sale as the next purchase price
                sum += prices[i] - buy;
                buy = prices[i];
            } else if (prices[i] + fee < buy) {
            	// Real buying price
                buy = prices[i] + fee;
            }
        }
        return sum;
    }
}

4, Two dimensional trade-off problem

135. Distribution of candy

Leetcode link

Solution:
Local optimization: if both sides consider together, they will consider one side at a time.

  1. Traverse from left to right. As long as the score on the right is greater than that on the left, the child on the right will have one more candy
  2. Traverse from right to left. As long as the score on the left is greater than that on the right, the child on the left will have one more candy
  3. Traverse again, and take the maximum value of the first two traversals for each position
class Solution {
    public int candy(int[] ratings) {
        int[] candyArr = new int[ratings.length];
        candyArr[0] = 1;
        // The number of candy for the first child is 1 by default, starting from the second child
        for (int i = 1; i < ratings.length; i++) {
            candyArr[i] = ratings[i] > ratings[i - 1] ? candyArr[i - 1] + 1 : 1;
        }
        // The backward forward traversal starts from the penultimate child, and the last position is the result of more than one round of traversal
        for (int i = ratings.length - 2; i >= 0; i--) {
            int temp = ratings[i] > ratings[i + 1] ? candyArr[i + 1] + 1 : 1;
            candyArr[i] = Math.max(temp, candyArr[i]);
        }
        int res = 0;
        for (int i = 0; i < candyArr.length; i++) {
            res += candyArr[i];
        }
        return res;
    }
}

406. The cohort was reconstructed according to height

Leetcode link
Suppose a group of people who are out of order stand in a queue, and the array people represents the attributes of some people in the queue (not necessarily in order). Each person [i] = [Hi, Ki] means that the height of the ith person is hi, and there is just ki person whose height is greater than or equal to hi in front. Please reconstruct and return the queue represented by the input array people. The returned queue should be formatted as an array queue, where queue[j] = [hj, kj] is the attribute of the jth person in the queue (queue[0] is the person in front of the queue).

Solution:
Sort by height before inserting

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        Arrays.sort(people, (p1, p2) -> {
        	// Same, in ascending order of the second element
            if (p1[0] == p2[0]) return p1[1] - p2[1];
            // Default descending order
            return p2[0] - p1[0];
        });
        List<int[]> List = new LinkedList<>();
        for (int[] arr : people) {
            List.add(arr[1], arr);
        }
        return List.toArray(new int[people.length][]);
    }
}

Topics: Algorithm leetcode linked list greedy algorithm