【贪心算法】整数替换,俄罗斯套娃信封问题,可被三整除的最大和,距离相等的条形码,重构字符串

文章目录

1. 整数替换(LC 397)

整数替换

题目描述

解题思路

模拟:递归+记忆化搜索

利用备忘录记录每个数到1的最小操作次数

  • 如果是偶数,返回n/2的值+1
  • 如果是奇数,返回n+1或n-1的最小值+!

贪心:找规律

知识补充:

  1. 偶数的二进制表示最后一位是0
  2. 奇数的二进制表示最后一位是1
  3. 计算机中的/2操作在二进制中就是整体左移

分类讨论:

  • n为偶数:只能/2,也就是左移
  • n为奇数:可以对n%4得到后两位的值
    • 如果后两位是01,-1操作更快到达1
    • 如果后两位是11,+1操作更快到达1
    • 如果n==3,-1再/2更快到达1

代码解析

  • 递归
java 复制代码
class Solution {
    HashMap<Long,Integer> hash = new HashMap<>();
    public int integerReplacement(int n) {
        hash.put((long)1,0);
        return dfs(n);
    }

    int dfs(long n){
        if(n==1)
            return 0;
        if(n%2==0){
            if(!hash.containsKey(n))
                hash.put(n,dfs(n/2)+1);
        }else{
            if(!hash.containsKey(n))
                hash.put(n,Math.min(dfs(n-1),dfs(n+1))+1);
        }
        return hash.get(n);
    }
}
  • 贪心
java 复制代码
public int integerReplacement(long n) {
        int ret = 0;
        while(n!=1){
            if(n==3){
                ret+=2;
                break;
            }
            if(n%2==0)
                n/=2;
            else{
                if(n%4==1)
                    n-=1;
                else if(n%4==3)
                    n+=1;
            }
            ret++;
        }
        return ret;
    }

2. 俄罗斯套娃信封问题(LC 354)

俄罗斯套娃信封问题

题目描述

解题思路

参考最长递增子序列 把乱序数组按照"套娃信封"的顺序排列,统计最长的套娃序列

动态规划

  • 状态表示:以当前的信封为结尾的最长套娃序列
  • 状态转移方程:从0遍历到当前位置,如果过可以接上,dp[i] = dp[j]+1;取最大值
  • 初始化:可以直接初始化为1
  • 返回值:整个dp表中的最大值

贪心+二分

  1. 先对数组的左端点排序,在左端点严格递增的情况下,只需要在右端点总找到最长递增子序列即可。
  2. 如果左端点相同,右端点就是乱序的,会干扰最长递增子序列的选择,因此要重写排序方法:左端点不同时,从小到大排序;左端点相同时,从大到小排序。
    • 举个例子:2,6 2,5 2,4 2,3 如果按照上面的逻辑,对于左端点相同的数组,不可能找到递增子序列。

代码解析

  • 动态规划(超时)
java 复制代码
public int maxEnvelopes(int[][] en) {
        int n = en.length;
        Arrays.sort(en,(a,b)->a[0]-b[0]);
        int[] dp = new int[n];
        Arrays.fill(dp,1);
        int ret = 1;
        for(int i = 1;i<n;i++){
            for(int j = 0;j<i;j++){
                if(en[j][0]<en[i][0] && en[j][1]<en[i][1])
                    dp[i] = Math.max(dp[i],dp[j]+1);
            }
            ret = Math.max(ret,dp[i]);
        }
        return ret;
    }
  • 贪心
java 复制代码
public int maxEnvelopes(int[][] en) {
        int n = en.length;
        if(n==1)
            return 1;
        Arrays.sort(en,(a,b)->{
            if(a[0]==b[0])
                return b[1]-a[1];
            return a[0] - b[0];
        });

        ArrayList<Integer> ret = new ArrayList<>();
        ret.add(en[0][1]);
        for(int i = 1;i<n;i++){
            if(en[i][1]>ret.getLast())
                ret.add(en[i][1]);
            else{
                int left = 0;
                int right = ret.size()-1;
                while(left<right){
                    int mid = left+(right-left)/2;
                    if(ret.get(mid)<en[i][1])
                        left = mid+1;
                    else
                        right = mid;
                }
                ret.set(left,en[i][1]);
            }
        }
        return ret.size();
    }

3. 可被三整除的最大和(LC 1262)

可被三整除的最大和

题目描述

解题思路

先计算所有数的总和,根据累加和,减去一部分数,直到可以被3整除。

  • sum%3==1 :有两种情况
    • 找到最小的x1,x1%3==1,删除x1
    • 找到最小的y1,y2。y1%3==2,y2%3==2,删除y1,y2
  • sum%3==2 :有两种情况
    • 找到最小的y1,y1%3==1,删除y1
    • 找到最小的y1,y2。x1%3==2,x2%3==2,删除x1,x2

根据上面的分析过程,统计和的同时,要找到x1,x2,y1,y2,分别是最小值与次小值

代码解析

java 复制代码
 public int maxSumDivThree(int[] nums) {
        int ret = 0;
        int n = nums.length;
        int x1 = 0x3f3f3f3f;
        int x2 = 0x3f3f3f3f;
        int y1 = 0x3f3f3f3f;
        int y2 = 0x3f3f3f3f;
        for(int i = 0;i<n;i++){
            ret+=nums[i];
            if(nums[i]%3==1 ){
                if(nums[i]<x1){
                    x2 = x1;
                    x1 = nums[i];
                }else if(nums[i]<x2)
                    x2 = nums[i];
            }

            if(nums[i]%3==2){
                if(nums[i]<y1){
                    y2 = y1;
                    y1 = nums[i];
                }else if(nums[i]<y2)
                    y2 = nums[i];
            }
        }
        if(ret%3==0)
            return ret;
        else if (ret%3==1)
            return ret - Math.min(x1,y1+y2);
        else
            return ret - Math.min(y1,x1+x2);
    }

4. 距离相等的条形码(LC 1054)

距离相等的条形码

题目描述

解题思路

  1. 利用哈希表记录每个数字出现的次数
  2. 先处理出现次数最多的数,每次摆放都与上一个空隔开一个位置.
  3. 最后排其他数字

代码解析

java 复制代码
public int[] rearrangeBarcodes(int[] barcodes) {
        int n = barcodes.length;
        int[] ret = new int[n];
        int maxcnt = 0;
        int maxnum = 0;
        HashMap<Integer,Integer> hash = new HashMap<>();
        for(int x:barcodes){
            hash.put(x,hash.getOrDefault(x,0)+1);
            if(hash.get(x)>maxcnt){
                maxnum = x;
                maxcnt = hash.get(x);
            }

        }

        int i = 0;
        while(hash.get(maxnum)>0){
            ret[i] = maxnum;
            hash.put(maxnum,hash.get(maxnum)-1);
            i+=2;
        }

        for(Map.Entry<Integer,Integer> entry:hash.entrySet()){
            int cnt = entry.getValue();
            while(cnt-- >0){
                if(i>=n)
                    i = 1;
                ret[i] = entry.getKey();
                i+=2;
            }
        }
        return ret;
    }

5. 重构字符串(LC 767)

重构字符串

题目描述

解题思路

遇上一题的思路完全相同,重排之前,要判断maxchar>(n+1)/2,如果成立,说明一定有相同的字符相邻

代码解析

java 复制代码
public String reorganizeString(String s) {
        int[] hash = new int[26];
        int n = s.length();
        char[] ss = s.toCharArray();

        char maxchar = '0';
        int maxcnt = 0;
        for(int i= 0;i<n;i++){
            hash[ss[i]-'a']++;
            if(hash[ss[i]-'a']>maxcnt){
                maxchar = ss[i];
                maxcnt = hash[ss[i]-'a'];
            }
        }
        if(maxcnt>(n+1)/2)
            return "";
            
        char[] ret = new char[n];
        int i = 0;
        while(hash[maxchar-'a']>0){
            ret[i] = maxchar;
            hash[maxchar-'a']--;
            i+=2;
        }

        for(int j= 0;j<26;j++){
            while(hash[j]>0){
                if(i>=n)
                    i = 1; 
                ret[i] = (char)(j+'a');
                hash[j]--;
                i+=2;
            }
        }
        return new String(ret);
    }
相关推荐
yzq1991271 小时前
语言在嵌入式系统中实现面向对象编程的实践与探索
算法
小欣加油1 小时前
leetcode3633 最早完成陆地和水上游乐设施的时间I
数据结构·c++·算法·leetcode
memcpy01 小时前
LeetCode 2657. 找到两个数组的前缀公共数组【集合,位运算】中等
算法·leetcode·职场和发展
计算机安禾1 小时前
【算法分析与设计】第37篇:平面扫描与线段交问题
java·大数据·数据库·算法·机器学习
8Qi81 小时前
LeetCode 236. 二叉树的最近公共祖先(LCA)
算法·leetcode·二叉树·递归·lca·后序遍历
兰令水1 小时前
leecodecode【二叉树排序+最近公共祖先】【2026.6.2打卡-java版本】
java·数据结构·算法·leetcode
人道领域1 小时前
【LeetCode刷题日记】77&&216.回溯算法剪枝优化在组合问题中的应用
java·算法·leetcode
Deepoch1 小时前
Deepoc数学大模型:以低幻觉特性护航半导体精准设计与制造
大数据·人工智能·算法·半导体·deepoc
诸葛务农1 小时前
共沸脱水技术及其在光刻胶用PGMEA纯化中的应用(上)
java·数据库·算法