0
点赞
收藏
分享

微信扫一扫

SpringMVC的架构有什么优势?——视图与模型(二)

夕阳孤草 2024-07-24 阅读 19

0、贪心算法介绍

image.png
image.png
image.png
例一中贪心策略可以得到最优解 image.png
例二中贪心策略找到的不是最优解
例三中贪心策略找到的也不是最优解

image.pngimage.png

1、柠檬水找零

image.png

class Solution {
    public boolean lemonadeChange(int[] bills) {
        // 0、仅有三个面额,20美元的纸币不会被用
        int five = 0, ten = 0;
        for(int x:bills){
            // 1、分类讨论
            // 1.1、遇见5直接收下
            if(x==5){
                five++;
            }else if(x == 10){
                // 1.2、遇见10收下,找出一张5美元
                five--;
                ten++;
            }else {
                // 1.3、遇见20美元,贪心的现有10美元找了,再用5美元找零
                if(ten>0&&five>0){
                    ten--;five--;
                } else {
                    five-=3;
                } 
            }
            // 2、尝试找零后的结果非法,那么就是找不开,返回false
            if(five<0 || ten<0){
                return false;
            }
        }
        return true;
    }   
}

image.png

2、将数组和减半的最小操作次数

image.png

class Solution {
    public int halveArray(int[] nums) {
        // 0、贪心策略:每次都让数组中最大的数减半
        double sum=0;
        PriorityQueue<Double> queue = new PriorityQueue<>((a, b) -> Double.compare(b, a));
        for(int num : nums){
            sum+=num;
            queue.add((double)num);
        } 
        sum/=2;
        int step=0;
        double deincrice=0;
        while(deincrice<sum){
            step++;
            double max = queue.poll()/2;
            deincrice+=max;
            queue.add(max);
        }
        return step;
    }
}

image.png

3、最大数 ★★★★

image.png

class Solution {
    public String largestNumber(int[] nums) {
        // 0、贪心策略:最高位越大、越靠前
        // 1、类似于字符串根据字典序排序一样,对nums进行排序,而不是按照大小排序
        int n = nums.length;
        String[] strs = new String[n];
        for(int i=0;i<n;i++) strs[i] = "" + nums[i];

        // 排序
        Arrays.sort(strs,(a,b)->{
            // 这个排序的规则才是最细节的地方
            return (b+a).compareTo(a+b);
        });

        StringBuffer ret = new StringBuffer();
        for(String s : strs) ret.append(s);
        if(ret.charAt(0)=='0') return "0";
        return ret.toString();
    }
}

image.png
image.png

4、摆动序列 ★★★★★★

image.png
image.png

class Solution {
    public int wiggleMaxLength(int[] nums) {
        int n = nums.length;
        if(n==1) return 1;
        int slope = 0; // 斜率,无所谓可以初始化为1或者-1
        Stack<Integer> wobble = new Stack<>();// 摆动序列wobble sequence
        wobble.push(nums[0]);
        int step = 0;
        for (int i = 1; i < n; i++) {
            if (wobble.peek() != nums[i]) {
                int temp = nums[i]-wobble.peek();
                if(slope*temp<=0) {
                    step++;
                    wobble.push(nums[i]);
                    slope = temp;
                }else{
                    wobble.pop();
                    wobble.push(nums[i]);
                }
            }
        }
        return step + 1;
    }
}
摆动暴动序列的特殊情况还挺多的,好难
class Solution {
    public int wiggleMaxLength(int[] nums) {
        // 返回最长子序列而不是子数组
        int n = nums.length;
        if (n == 1)
            return 1;
        int direction = 0;// 代表斜率,等于1或者-1
        int step = 1;// 步数
        int i = 0; //遍历数组
        // 例如:5552637
        // 这段while循环就是为了遍历到开头平底的最后一个位置
        while (i + 1 < n) {
            if (nums[i] == nums[i + 1]) { // 平地则不考虑
                i++;
            } else {
                // 维护斜率
                direction = nums[i] < nums[i + 1] ? 1 : -1;
                step++;
                break;
            }
        }
        // 继续遍历数组,每一次斜率变化进行步数加1
        for (i++; i + 1 < n; i++) {
            if (nums[i] != nums[i + 1]) {
                int temp = 0;
                temp = nums[i] < nums[i + 1] ? 1 : -1;
                if (direction != temp) {
                    direction = temp;
                    step++;
                }
            }
        }
        return step;
    }
}
class Solution {
    public int wiggleMaxLength(int[] nums) {
        int n = nums.length;
        if (n < 2)
            return n;
        int ret = 0, left = 0;
        for (int i = 0; i < n - 1; i++) {
            // 如果⽔平,直接跳过
            if (nums[i + 1] != nums[i]){
                int right = nums[i + 1] - nums[i]; // 计算接下来的趋势
                if (left * right <= 0){
                    ret++; // 累加波峰或者波⾕
                } 
                left = right;
            }
        }
        return ret + 1;
    }
}

image.png

5、最长递增子序列★★★★★

image.png
贪心策略:
image.png

image.png

class Solution {
    public int lengthOfLIS(int[] nums) {
        ArrayList<Integer> ret = new ArrayList<>();
        int n = nums.length;
        ret.add(nums[0]);
        for (int i = 1; i < n; i++) {
            if (nums[i] > ret.get(ret.size() - 1)) // 如果能接在最后⼀个元素后⾯,直接放
            {
                ret.add(nums[i]);
            } else {
                // ⼆分插⼊位置
                int left = 0, right = ret.size() - 1;
                while (left < right) {
                    int mid = (left + right) / 2;
                    if (ret.get(mid) < nums[i])
                        left = mid + 1;
                    else
                        right = mid;
                }
                ret.set(left, nums[i]); // 放在 left 位置上
            }
        }
        return ret.size();
    }
}

class Solution {
    public int lengthOfLIS(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n+1];
        int ret = 0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<i;j++){
                if(nums[i-1]>nums[j-1]){
                    dp[i] = Math.max(dp[i],dp[j]);
                }
            }
            dp[i] += 1;
            ret = Math.max(ret,dp[i]);
        }
        return ret;

    }
}
// 时间复杂度O(N^2)

6、递增的三元子序列★★★★★

image.png

image.png

class Solution {
    public boolean increasingTriplet(int[] nums) {
        int n = nums.length;
        int a = nums[0], b = Integer.MAX_VALUE;
        for (int i = 1; i < n; i++) {
            if (nums[i] < a) // 如果能接在最后⼀个元素后⾯,直接放
            {
                a = nums[i];
            } else if (b < nums[i]) {
                return true;
            } else {
                b = nums[i];
            }
        }
        return false;
    }
}

7、最长连续递增序列

image.png

class Solution {
    public int findLengthOfLCIS(int[] nums) {
        // 双指针
        int n = nums.length;
        int left = 0, right = 1;
        int max = 1;
        while(right<n){
            if(nums[right]>nums[right-1]){
                right++;
                max = Math.max(max,right-left);
            }else{
                left=right;
                right++;
            }
        }
        return max;
    }
}

8、买卖股票的最佳时机

image.png

class Solution {
    int[] temp;
    public int maxProfit(int[] prices) {
        // 二分法
        int n = prices.length;
        temp = new int[n];
        return mergeSort(prices,0,n-1);
    }
    public int mergeSort(int[] prices ,int left,int right){
        if(left>=right) return 0;
        int mid = left + (right - left)/2;
        // 0、归并排序求最大差值
        int max_left = mergeSort(prices,left,mid);
        int max_right = mergeSort(prices,mid+1,right);
        int max = Math.max(max_left,max_right);
        // 1、返回值,三者的最大值
        max = Math.max(max,prices[right]-prices[left]);   
        // 排序
        int i=left,j=mid+1,k=0;
        while(i<=mid && j<=right){
            if(prices[i] < prices[j]){
                temp[k++] = prices[i++];
            }else{
                temp[k++] = prices[j++];
            }
        }
        while(i<=mid){
            temp[k++] = prices[i++];
        }
        while(j<=right){
            temp[k++] = prices[j++];
        }
        for(k=0;k<right-left+1;k++){
            prices[k+left] = temp[k];
        }
        return max;
    }
}
// 时间复杂度:// O(NLogN)
class Solution {
    int[] temp;

    public int maxProfit(int[] prices) {
        // 二分法
        int n = prices.length;
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
        maxHeap.add(prices[0]);
        int max = 0;
        for (int i = 1; i < n; i++) {
            max = Math.max(max, prices[i] - maxHeap.peek());
            maxHeap.add(prices[i]);
            maxHeap.poll();
        }
        return max;
    }
}
// 小根堆也可以,但是小根堆会存储prices的所有元素,没必要
// 保持大根堆的大小为1即可
// 时间复杂度:O(Nlogn),比分治优化了一点
// 使用小根堆的解法时间复杂度也是O(NlogN)
class Solution {
    public int maxProfit(int[] prices) {
        // 贪心贪的不明显
        int n = prices.length;
        int min = prices[0];
        int max = 0;
        for (int i = 1; i < n; i++) {
            max = Math.max(max, prices[i] - min); // 先更新最大收益
            min = Math.min(min,prices[i]); // 再更新最低价
        }
        return max;
    }
}
// 没必要使用大根堆,使用一个变量去记录前驱的最小值即可。
// 时间复杂度为O(N)
// 动态规划也能解决

9、买卖股票的最佳时机II

image.png
image.png

class Solution {
    public int maxProfit(int[] prices) {
        // 可以买卖多次
        // 只要明天比今天股价高,那么就进行一次今天买,明天卖,每一次利润都争取
        int max = 0;
        int i = 0, j = 0, n = prices.length;
        while (j < n - 1) {
            if (prices[j] < prices[j+1]) {
                max += prices[j+1] - prices[j];
            }
            j++;
        }
        return max;
    }
}

10、k次取反后最大化的数组和

image.png

class Solution {
    public int largestSumAfterKNegations(int[] nums, int k) {
        // 遇见负数就取反
        // 情况好多,一趟遍历搞不定,艹,不是我无能。
        int m = 0, minElem = Integer.MAX_VALUE, n = nums.length;
        for (int x : nums) {
            if (x < 0)
                m++;
            minElem = Math.min(minElem, Math.abs(x));
        }
        // 分类讨论
        int ret = 0;
        if (m > k) {
            Arrays.sort(nums);
            for (int i = 0; i < k; i++) // 前 k ⼩个负数,变成正数
            {

                ret += -nums[i];
            }
            for (int i = k; i < n; i++) // 后⾯的数不变
            {
                ret += nums[i];
            }
        } else {
            // 把负数全部变成正数
            for (int x : nums)
                ret += Math.abs(x);
            if ((k - m) % 2 != 0) // 判断是否处理最⼩的正数
            {
                ret -= minElem * 2;
            }
        }
        return ret;

        // 为什么一趟遍历搞不定呢?
    }
}

image.png

11、按身高排序 ★★

image.png

class Solution {
    public String[] sortPeople(String[] names, int[] heights) {
        // 1. 创建⼀个下标数组
        int n = names.length;
        Integer[] index = new Integer[n];
        for (int i = 0; i < n; i++)
            index[i] = i;
        // 2. 对下标数组排序
        Arrays.sort(index, (i, j) -> {
            return heights[j] - heights[i];
        });
        // 3. 提取结果
        String[] ret = new String[n];
        for (int i = 0; i < n; i++) {
            ret[i] = names[index[i]];
        }
        return ret;
    }
}

12、优势洗牌(田忌赛马)

image.png

class Solution {
    public int[] advantageCount(int[] nums1, int[] nums2) {
        int n = nums1.length;
        // 0、创建下标数组
        Integer[] index2 = new Integer[n];
        // 0.1、初始化下标数组
        for(int i=0;i<n;i++) index2[i]=i;
        Arrays.sort(nums1);
        Arrays.sort(index2, (i, j) -> {
            return nums2[i] - nums2[j];
        });
        int[] ret = new int[n];
        int i=0;
        int left=0,right=n-1;
        while(left<=right){ // 或者写成 i<n
            if(nums1[i] > nums2[index2[left]]){
                // 贪心策略:出最小的大于nums2的数
                ret[index2[left++]] = nums1[i++];
            }else{
                // 贪心策略:下等马消耗上等马
                ret[index2[right--]] = nums1[i++];
            }
        }
        return ret;
    }
}
// 2 7 11 15 排序的nums1
// 1 10 4 11 //nums2
// 0 2 1  3  //index2


// 8  12 24 32
// 11 13 25 32

image.png

13、最长回文串

image.png

class Solution {
    public int longestPalindrome(String s) {
        // 出现以此的字符只能去一个
        // 出现过偶数次的字符可以去偶数个
        int center = 0;
        int[] hash = new int[52];
        for(int i=0;i<s.length();i++){
            char ch = s.charAt(i);
            if('a'<=ch && ch<='z') hash[ch-'a']++;
            if('A'<=ch && ch<='Z') hash[ch-'A'+26]++;
        }
        int sum = 0;
        int count = 0;
        for(int i=0;i<52;i++){
            sum+=hash[i];
            if(hash[i]%2==1){
                count++;
            }
        }
        if(count!=0) return sum - count +1;
        else return sum;
    }
}
// s只包含大小写字母,不含其他特殊字符
class Solution {
    public int longestPalindrome(String s) {
        // 1. 计数 - ⽤数组模拟哈希表
        int[] hash = new int[127];
        for (int i = 0; i < s.length(); i++) {
            hash[s.charAt(i)]++;
        }
        // 2. 统计结果
        int ret = 0;
        for (int x : hash) {
            ret += x / 2 * 2;
        }
        return ret < s.length() ? ret + 1 : ret;
    }
}
// 吴老师代码,好精简

14、增减字符串匹配

image.png

class Solution {
    public int[] diStringMatch(String s) {
        // 0,1,2,3,4
        // 两个DD 就取最大的两个值,需要用的时候,随便用
        int left = 0, right = s.length();
        int[] ret = new int[right + 1];
        int i = 0;
        while (left < right) {
            char ch = s.charAt(i);
            if (ch == 'I')
                ret[i++] = left++;
            if (ch == 'D')
                ret[i++] = right--;
        }
        ret[i] = left;
        return ret;
    }
}

15、分发饼干

image.png

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        int m = g.length;
        int n = s.length;
        Arrays.sort(g);
        Arrays.sort(s);
        int ret = 0;
        int i=0,j=0;
        while(i<m&&j<n){
            if(g[i]<=s[j]){
                ret++;
                i++;
                j++;
            }else{
                j++;
            }
        }
        return ret;
    }
}
class Solution {
    public int findContentChildren(int[] g, int[] s) {
        int m = g.length, n = s.length;
        Arrays.sort(g);
        Arrays.sort(s);
        int ret = 0;
        for (int i = 0, j = 0; i < m && j < n; j++) {
            if (g[i] <= s[j]) {
                ret++;
                i++;
            }
        }
        return ret;
    }
}

16、最优除法★★★★

image.png
image.png

image.png

class Solution {
    public String optimalDivision(int[] nums) {
        int n = nums.length;
        if(n==1) return nums[0]+"";
        if(n==2) return nums[0]+"/"+nums[1];
        
        StringBuffer str = new StringBuffer();
        for(int i=0;i<n;i++){
            if(i==0){
                str.append(nums[i]+"/(");
            }else if(i==n-1){
                str.append(nums[i]);
            }else{
                str.append(nums[i]+"/");
            }
        }
        str.append(")");
        return str.toString();
    }
}

17、跳跃游戏★★★★

image.png

class Solution {
    public int jump(int[] nums) {
        int ret = 0;
        int n = nums.length;
        int[] setp = new int[n];
        setp[n-1]=0;
        for(int i=n-2;i>=0;i--){
            int min = Integer.MAX_VALUE;
            for(int j=1;j<=nums[i]&&i+j<n;j++){
                min = Math.min(min,setp[i+j]);
            }
            setp[i] = min==Integer.MAX_VALUE?min:min+1;
        }
        return setp[0];

    }
}
// 动态规划,时间复杂度O(N^2)

image.png

class Solution {
    public int jump(int[] nums) {
        // 类似于层序遍历
        int left = 0, right = 0, ret = 0, maxPos = 0, n = nums.length;
        while (left <= right) // 以防跳不到 n - 1 的位置
        {
            if (maxPos >= n - 1) // 判断是否已经能跳到最后⼀个位置
            {
                return ret;
            }
            for (int i = left; i <= right; i++) {
                // 更新下⼀层的最右端点
                maxPos = Math.max(maxPos, nums[i] + i);
            }
            left = right + 1;
            right = maxPos;
            ret++;
        }
        return -1;
    }
}

// 也是正向查找,思想是一致的,官方写的更优雅
class Solution {
    public int jump(int[] nums) {
        int length = nums.length;
        int end = 0;
        int maxPosition = 0; 
        int steps = 0;
        for (int i = 0; i < length - 1; i++) {
            maxPosition = Math.max(maxPosition, i + nums[i]); 
            if (i == end) {
                end = maxPosition;
                steps++;
            }
        }
        return steps;
    }
}

作者:力扣官方题解
链接:https://leetcode.cn/problems/jump-game-ii/solutions/230241/tiao-yue-you-xi-ii-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

正向跳跃示意图

反向查找,但也是类似层序的思想
class Solution {
    public int jump(int[] nums) {
        int position = nums.length - 1;
        int steps = 0;
        while (position > 0) {
            for (int i = 0; i < position; i++) {
                if (i + nums[i] >= position) {
                    position = i;
                    steps++;
                    break;
                }
            }
        }
        return steps;
    }
}
// 存在重复遍历

作者:力扣官方题解
链接:https://leetcode.cn/problems/jump-game-ii/solutions/230241/tiao-yue-you-xi-ii-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

反向查找示意图

18、跳跃游戏

image.png

class Solution {
    public boolean canJump(int[] nums) {
        int left = 0, right = 0, ret = 0, maxPos = 0, n = nums.length;
        while (left <= right) // 以防跳不到 n - 1 的位置
        {
            if (maxPos >= n - 1) // 判断是否已经能跳到最后⼀个位置
            {
                return true;
            }
            for (int i = left; i <= right; i++) {
                // 更新下⼀层的最右端点
                maxPos = Math.max(maxPos, nums[i] + i);
            }
            left = right + 1;
            right = maxPos;
            ret++;
        }
        return false;
    }
}

19、加油站

image.png

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        // 环绕一周
        int c = 0;
        int index = 0;
        int ret  = index;
        int n = gas.length;
        while(ret<=index){
            // 到地方加油
            c+=gas[index%n];
            c-=cost[index%n];
            // 判断能否驶入到下一站点
            if(c<0){
                // 不能则说明从index之前的任何一个站点出发都不可能环游一周
                // 从index+1位置重新开始判断
                index++;
                ret = index%n;
                c=0;
                // 说明这个位置被开启了第二次判断
                if(index>ret) return -1;
            }else{
                if(ret == index%n && index>ret) return ret;
                else index++;
            }
        }
        return 0;
    }
}
class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int n = gas.length;
        for (int i = 0; i < n; i++) // 依次枚举所有的起点
        {
            int rest = 0; // 统计净收益
            int step = 0;
            for (; step < n; step++) // 枚举向后⾛的步数
            {
                int index = (i + step) % n; // ⾛ step 步之后的下标
                rest = rest + gas[index] - cost[index];
                if (rest < 0) {
                    break;
                }
            }
            if (rest >= 0) {

                return i;
            }
            i = i + step; // 优化
        }
        return -1;
    }
}

20、单调递增的数字

image.png

class Solution {
    public int monotoneIncreasingDigits(int n) {
        List<Integer> arr = new ArrayList<>();
        while(n>0){
            arr.add(n%10);
            n/=10;
        }
        int m = arr.size();
        for(int i=0;i<m-1;i++){
            int cur = arr.get(i);
            int pre = arr.get(i+1);
            if(cur < pre){
                int j=i;
                while(j>=0 && arr.get(j)!=9 ) arr.set(j--,9);
                arr.set(i+1,pre-1);
            }
        }
        int num = 0;
        for(int i=m-1;i>=0;i--){
            num=num*10+arr.get(i);
        }
        return num;
    }
}
// 从个位开始遍历,遇见高位数字pre大于低位数字cur时就将高位更新为pre-1,低于该为的全部更新为9
class Solution {
    public int monotoneIncreasingDigits(int n) {
        // 把数字转化成字符串
        char[] s = Integer.toString(n).toCharArray();
        int i = 0, m = s.length;
        // 找第⼀个递减的位置
        while (i + 1 < m && s[i] <= s[i + 1])
            i++;
        if (i == m - 1)
            return n; // 特判⼀下特殊情况
        // 回退
        // 回退可以提前使用一个变量进行标记
        while (i - 1 >= 0 && s[i] == s[i - 1])
            i--;
        s[i]--;
        for (int j = i + 1; j < m; j++)
            s[j] = '9';
        return Integer.parseInt(new String(s));
    }
}

// int n = 12345550
// int[] nums = [1,2,3,4,5,5,5,0]


class Solution {
    public int monotoneIncreasingDigits(int n) {
        // 把数字转化成字符串
        char[] s = Integer.toString(n).toCharArray();
        int i = 0, m = s.length;
        int start = 0;
        // 找第⼀个递减的位置
        while (i + 1 < m ){
            if(s[i] == s[i + 1]){
                i++;
            }else if(s[i] < s[i + 1]){
                i++;
                start = i;
            }else{
                break;
            }
        }
        if (i == m - 1)
            return n; // 特判⼀下特殊情况
        s[start]--;
        for (int j = start+1; j < m; j++)
            s[j] = '9';
        return Integer.parseInt(new String(s));
    }
}

21、坏了的计算器★★★★★★★

image.png

class Solution {
    public int brokenCalc(int startValue, int target) {
        if(target<startValue) return startValue-target;
        int setp = 0;
        while(target>startValue){
            if(target%2==0) target/=2;
            else target+=1;
            setp++;
        }
        return setp + startValue -target;
    }
}
// 为什么这么写时正确的?

22、合并区间

image.png

public class Solution {
    public int[][] merge(int[][] intervals) {
        // 首先对区间按照起始时间进行排序
        Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
        
        List<int[]> merged = new ArrayList<>();
        
        // 遍历排序后的区间
        for (int[] interval : intervals) {
            // 如果合并后的区间列表为空, 或者当前区间的起始时间大于合并后区间列表中最后一个区间的结束时间,
            // 则直接将当前区间加入合并后的区间列表
            if (merged.isEmpty() || interval[0] > merged.get(merged.size() - 1)[1]) {
                merged.add(interval);
            } 
            // 否则,将当前区间与合并后区间列表中最后一个区间进行合并
            else {
                merged.get(merged.size() - 1)[1] = Math.max(merged.get(merged.size() - 1)[1], interval[1]);
            }
        }
        // 将合并后的区间列表转换为数组并返回
        return merged.toArray(new int[merged.size()][]);
    }
}

23、无重叠区间

image.png

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        // 1、排序
        // 2、如果两个区间有交集,删除左端点大的区间,若左端点一样大,则删除区间长的那个
        Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
        int ret = 0;
        List<int[]> merged = new ArrayList<>(); 
        for (int[] interval : intervals) {
            // 新区间不与最后一个区间冲突,那么就直接add
            if (merged.isEmpty() || interval[0] >= merged.get(merged.size() - 1)[1]) {
                merged.add(interval);
            }else { 
                // 如果新区间与最后一个区间冲突,那么一定不与倒数第二个区间冲突,
                // 所以为了更大可能防止于后续的区间冲突,就选择右端点更小的区间进行保留。
                if(interval[1] < merged.get(merged.size() - 1)[1]){
                    merged.set(merged.size()-1,interval);
                }
                ret++;
            }
        }
        return ret;
    }
}
class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        // 1、排序
        // 2、如果两个区间有交集,删除右端点大的区间,以尽可能地避免后续区间发生冲突
        Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
        int ret = 0;
        int right = intervals[0][0];
        for (int i=0;i<intervals.length;i++) {
            int[] interval = intervals[i];
            if (interval[0] >= right) {
                right = interval[1];
            }else { 
                if(interval[1] < right){
                    right = interval[1];
                }
                ret++;
            }
        }
        return ret;
    }
}

24、用最少量的箭引爆气球

image.png

public class Solution {
    public int findMinArrowShots(int[][] points) {
        // 根据右端点进行排序
        Arrays.sort(points, (a, b) -> a[0] > b[0] ? 1 : -1);
        int num = 0;
        int left = points[0][0];
        int right = points[0][1];

        for (int i = 0; i < points.length; i++) {
            // 贪心策略:如果有重叠,缩小射箭区间,,为了可以同时引爆多个气球
            // 每一只箭都射击了尽可能多的气球
            int a = points[i][0], b = points[i][1];
            if (a <= right) {
                left = a;
                if (b < right)
                    right = b;
            } else {
                num++;
                left = a;
                right = b;
            }
        }
        return num + 1;
    }
}

25、整数替换

image.png

class Solution {
    Map<Long,Integer> hash;
    public int integerReplacement(int n) {
        hash = new HashMap<>();
        return integerReplacement2(n*1L);
    }
    public int integerReplacement2(long n) {
        if(hash.containsKey(n)) return hash.get(n); 
        if(n<=1) return 0;
        if(n%2==1){
            int a = integerReplacement2(n-1);
            int b = integerReplacement2(n+1);// n+1可能爆int
            int ret = Math.min(a,b)+1;
            hash.put(n,ret);
            return ret;
        }else{
            int c = integerReplacement2(n/2)+1;
            hash.put(n,c);
            return c;
        }
    }
}
class Solution {
    Map<Long,Integer> hash;
    public int integerReplacement(int n) {
        hash = new HashMap<>();
        return integerReplacement2(n*1L);
    }
    public int integerReplacement2(long n) {
        if(n<=1) return 0;
        if(n%2==1){
            int a = hash.getOrDefault(n,integerReplacement2(n-1));
            int b = hash.getOrDefault(n,integerReplacement2(n+1));// n+1可能爆int
            int ret = Math.min(a,b)+1;
            hash.put(n,ret);
            return ret;
        }else{
            int c = hash.getOrDefault(n,integerReplacement2(n/2)+1);
            hash.put(n,c);
            return c;
        }
    }
}
// 递归层数太多了,可以使用记忆化搜索

image.png
image.png

class Solution {
    // 贪心算法:
    public int integerReplacement(int n) {
        int step = 0;
        while (n > 1) {
            if (n % 2 == 0) {
                n /= 2;
                step++;
            } else {
                if (n == 3) {
                    return step + 2;
                } else {
                    if (n % 4 == 1) {
                        n /= 2;
                        step += 2;
                    } else {
                        n = n / 2 + 1; // (n+1)/2 可能会溢出
                        step += 2;
                    }
                }
            }
        }
        return step;
    }
}

26、俄罗斯套娃信封问题

image.png

class Solution {
    public int maxEnvelopes(int[][] e) {
        // 解法⼀:动态规划
        Arrays.sort(e, (v1, v2) -> {
            return v1[0] - v2[0];
        });
        int n = e.length;
        int[] dp = new int[n];
        int ret = 1;
        for (int i = 0; i < n; i++) {
            dp[i] = 1; // 初始化
            for (int j = 0; j < i; j++) {
                if (e[i][0] > e[j][0] && e[i][1] > e[j][1]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            ret = Math.max(ret, dp[i]);
        }
        return ret;
    }
}
// 动态规划 
// 时间复杂度O(N^2)。dp[i]代表以第i封信为结尾的最高层数
// 这个解法和最后一种只对左端点排序的解法很像
class Solution {
    public int maxEnvelopes(int[][] envelopes) {
        Arrays.sort(envelopes, (a, b) -> {
            if (a[0] != b[0]) {
                return a[0] < b[0] ? -1 : 1;
            }
            return a[1] > b[1] ? -1 : 1;
        });
        int m = envelopes.length;
        List<Integer> ret = new ArrayList<>();
        ret.add(envelopes[0][1]);
        for (int i = 0; i < m; i++) {
            int b = envelopes[i][1];
            if (b > ret.get(ret.size() - 1)) {
                ret.add(b);
            } else {
                int left = 0, right = ret.size() - 1;
                // 二分查找第一个大于等于b的数字
                while (left < right) {
                    int mid = left + (right - left) / 2;
                    if (ret.get(mid) < b) {
                        left = mid+1;
                    } else {
                        right = mid;
                    }
                }
                ret.set(left, b);
            }
        }
        return ret.size();
    }
}

// 重排序+贪心+二分

27、可被三整除地最大和 ★★★★(正难则反)

image.png

class Solution {
    public int maxSumDivThree(int[] nums) {
        // 先找出所有三的倍数
        // 遍历
        // 如果a%3+b%3==3,那么(a+b)%3==0
        // 如果a%3==b%3==c%3==1,那么(a+b+c)%3==0
        // [4,4,4]也可以。艹,这个策略不太行
        
        // 先把所有数累加
        
        // 若sum%3==0,则return sum;
        // 若sum%3==1,则删除一个最小和x,s%3==1
        // 若sum%3==2,则删除一个最小和x,s%3==2

        //对所有数分类
        // 1 4 7
        // 3 6 9
        // 2 5 8
        // 若sum%3==1,则删除一个模3等于一的数字,或者两模3等于2的数字
        // 若sum%3==2,则删除一个模3等于二的数字,或者两模3等于1的数字
        Arrays.sort(nums);
        List<Integer> yu1 = new ArrayList<>();
        List<Integer> yu2 = new ArrayList<>();
        int sum=0;
        for(int i=0;i<nums.length;i++){
            sum+=nums[i];
            if(nums[i]%3==1) yu1.add(nums[i]);
            if(nums[i]%3==2) yu2.add(nums[i]);
        }
        if(sum%3==0){
            return sum;
        }else if(sum%3==1){
            int min = sum;
            if(yu1.size()>=1){
                min = yu1.get(0);
            }
            if(yu2.size()>=2){
                min = Math.min(min,yu2.get(0)+yu2.get(1));
            }
            return sum-min;
        }else{
            int min = sum;
            if(yu1.size()>=2){
                min = yu1.get(0)+yu1.get(1);
            }
            if(yu2.size()>=1){
                min = Math.min(min,yu2.get(0));
            }
            return sum-min;
        }
    }
}
class Solution {
    public int maxSumDivThree(int[] nums) {
        int INF = 0x3f3f3f3f;
        int sum = 0, x1 = INF, x2 = INF, y1 = INF, y2 = INF;
        // 使用四个变量存储最小值和次小值
        for (int x : nums) {
            sum += x;
            if (x % 3 == 1) {
                if (x < x1) {
                    x2 = x1;
                    x1 = x;
                } else if (x < x2) {
                    x2 = x;
                }
            } else if (x % 3 == 2) {
                if (x < y1) {
                    y2 = y1;
                    y1 = x;
                } else if (x < y2) {
                    y2 = x;
                }
            }
        }
        // 分类讨论
        if (sum % 3 == 0)
            return sum;
        else if (sum % 3 == 1)
            return Math.max(sum - x1, sum - y1 - y2);
        else
            return Math.max(sum - y1, sum - x1 - x2);
    }
}
// 钻了数据范围的漏洞,因为x1,x2,y1,y2是有可能还能与INF,
// 分类讨论时存在INF的哪一项结果为负数,不可能被return
// 如果sum % 3 == 0,那有变量不等于INF,所以一定会返回正数
// 无论是几的倍数能都解决

28、距离相等的条形码

image.png

// 把这些分类存储起来,然后构造返回值
// 贪心策略:尽量某两个数交替,而不是三个数进行交替

// 出现次数最多的数字x
// 其他数
// 先在偶数位置放置x
// 因为题目保证有解,所以x的数量最多是数组的一半
// 所以当x填入偶数位置以后,其他数按照顺序填入是不可能出现相同数字相邻的情况。
class Solution {
    public int[] rearrangeBarcodes(int[] b) {
         
        int n = b.length;
        int[] ret = new int[n];
        int index = 0;
        // 先处理出现次数最多的那个数
        for (int i = 0; i < maxCount; i++) {
            ret[index] = maxVal;

            index += 2;
        }
        hash.remove(maxVal);
        for (int x : hash.keySet()) {
            for (int i = 0; i < hash.get(x); i++) {
                if (index >= n)
                    index = 1;
                ret[index] = x;
                index += 2;
            }
        }
        return ret;
    }
}

29、重构的字符串

image.png

class Solution {
    public String reorganizeString(String s) {
        // 只要出现次数最多的的那个元素不超过一半就可以重新排序
        // 统计每个数出现了多少次
        int n = s.length();
        int[] hash = new int[26];        
        char maxChar = ' ';int maxCount = 0;
        for (int i=0;i<n;i++) {
            char ch = s.charAt(i);
            hash[ch-'a']++;
            if (maxCount < hash[ch-'a']) {
                maxChar = ch;
                maxCount = hash[ch-'a'];
            }
        }
        if(2*maxCount>n+1) return "";

        char[] str = new char[n];
        // 先填充出现次数最多的字符
        int index = 0;
        for(int i=0;i<maxCount;i++){
            str[index]=maxChar;
            index+=2;
        }
        // 移除该字符
        hash[maxChar-'a']=0;
        // 填充其他字符
        for(int i=0;i<26;i++){
            for(int j=0;j<hash[i];j++){
                if(index>=n) index=1;
                str[index]=(char)('a'+i);
                index+=2;
            }
        }
        return new String(str);
    }
}
举报

相关推荐

0 条评论