leetcode(494/3/53/135/97/72/772/11/301/300/45/517/1643/146/42/407)面试题08.14~剑指offer62

本文详细解析了多个LeetCode和腾讯面试题,涉及算法和数据结构,包括有序数组、字符串操作、数组操作、动态规划、二叉树、编辑距离、跳跃游戏等问题,旨在帮助读者提升编程能力。
摘要由CSDN通过智能技术生成

目录

题目1:给定一个有序数组arr,代表坐落在X轴上的点,给定一个正数K,代表绳子的长度。返回绳子最多压中几个点?即使绳子边缘处盖住点也算盖住。

题目2:一个数组中只有俩个字符'G'和'B',可以让所有的G都放在左侧,所有的B都放在右侧。或者可以让所有的G都放在右侧,所有的B都放在左侧。但是只能在相邻字符之间进行交换操作,问返回至少需要交换几次。

题目3:(leetcode494)给定一个数组arr,你可以在每个数字之前决定+或者-,但是必须所有数字都参与。再给定一个数target,请问最后算出target的方法数是多少。

题目4:现有司机M人,调度中心会将所有司机平分给A、B俩个区域。第i个司机去A可得到收入为income[i][0],第i个司机去B可得到收入为income[i][2],那么返回所有调度方案中能使所有司机总收入最高的方案,是多少钱。

题目5:HashMap实现setAll的功能。

题目6:(leetcode3)求一个字符串中,最长无重复字符子串长度

题目7:给定一个数组arr,代表每个人的能力值。在给定一个非负数k,如果俩个人能力差值正好为k,那么可以凑在一起比赛。一局比赛只有俩个人,返回最多可以同时有多少场比赛

题目8:(腾讯原题)给定一个正数数组arr,代表若干人的体重。再给定一个正数limit,表示所有船共同拥有的载重量。每艘船最多做俩人,且不能超过载重。想让所有的人同时过河,并且用最好的分配方法让船尽量少,返回最少的船数。

题目9:(leetcode53)返回一个数组中,子数组最大累加和

题目10:(leetcode135)分发糖果问题。

 题目11:(leetcode97)字符串交错组成

题目12:如果一个节点X,它左树结构和右树结构完全一样。那么我们说以X为头的树是相等树,给定一颗二叉树的头节点head。返回head整棵树上有多少棵相等子树。

题目13:(leetcode72)编辑距离问题

题目14:(leetcode772)

题目15:(leetcode11)盛最多水的容器

题目16:(leetcode301)给你一个由若干括号和字母组成的字符串s,删除最小数量的无效括号,使得输入的字符串有效,返回所有可能的结果。

 题目17:(leetcode300)最长递增子序列长度

题目18:定义何为step num?比如680,680+68+6=754,680的step num叫754。给定一个正数num,判断它是不是某个数的step num

题目19:(leetcode45)跳跃游戏Ⅱ

题目20:面试题 08.14. 布尔运算 - 力扣(LeetCode)

题目21:(leetcode517)超级洗衣机问题

题目22:约瑟夫环问题-剑指 Offer 62. 圆圈中最后剩下的数字 - 力扣(LeetCode)

题目23:(leetcode1643)第K条最小指令

题目24:(leetcode146)LRU

题目25:(leetcode42)一维接雨水

题目26:(leetcode407)二维接雨水


题目1:给定一个有序数组arr,代表坐落在X轴上的点,给定一个正数K,代表绳子的长度。返回绳子最多压中几个点?即使绳子边缘处盖住点也算盖住。

解题思路:用俩个指针L和R,判断它们的距离差,看情况R++或者L++。

时间复杂度是O(N)

    public static int maxPoint2(int[] arr, int L) {
        int left = 0;
        int right = 0;
        int N = arr.length;
        int max = 0;
        while (left < N) {
            while (right < N && arr[right] - arr[left] <= L) {
                right++;
            }
            max = Math.max(max, right - (left++));
        }
        return max;
    }

题目2:一个数组中只有俩个字符'G'和'B',可以让所有的G都放在左侧,所有的B都放在右侧。或者可以让所有的G都放在右侧,所有的B都放在左侧。但是只能在相邻字符之间进行交换操作,问返回至少需要交换几次。

解题思路:通过指针的方式去求解。

    public static int minSteps2(String s) {
        if (s == null || s.equals("")) {
            return 0;
        }
        char[] str = s.toCharArray();
        int step1 = 0;
        int step2 = 0;
        int gi = 0;
        int bi = 0;
        for (int i = 0; i < str.length; i++) {
            if (str[i] == 'G') { // 当前的G,去左边   方案1
                step1 += i - (gi++);
            } else {// 当前的B,去左边   方案2
                step2 += i - (bi++);
            }
        }
        return Math.min(step1, step2);
    }

题目3:(leetcode494)给定一个数组arr,你可以在每个数字之前决定+或者-,但是必须所有数字都参与。再给定一个数target,请问最后算出target的方法数是多少。

第一种方式暴力递归

    public static int findTargetSumWays1(int[] arr, int s) {
        return process1(arr, 0, s);
    }


    public static int process1(int[] arr, int index, int rest) {
        if (index == arr.length) { 
            return rest == 0 ? 1 : 0;
        }

        return process1(arr, index + 1, rest - arr[index]) + process1(arr, index + 1, rest + arr[index]);
    }

第二种方式记忆化搜索(动态规划)

    public static int findTargetSumWays2(int[] arr, int s) {
        return process2(arr, 0, s, new HashMap<>());
    }
    //HashMap<Integer, HashMap<Integer, Integer>> dp
    // index + rest
    // index == 7 rest = 13   256
    // index == 7 rest = 35   17
    //{
    //	7 :  { 13 , 256}
    //	  :  { 35 , 17}
    //}
    public static int process2(int[] arr, int index, int rest, HashMap<Integer, HashMap<Integer, Integer>> dp) {
        if (dp.containsKey(index) && dp.get(index).containsKey(rest)) {
            return dp.get(index).get(rest);
        }
        // 否则,没命中!
        int ans = 0;
        if (index == arr.length) {
            ans = rest == 0 ? 1 : 0;
        } else {
            ans = process2(arr, index + 1, rest - arr[index], dp) + process2(arr, index + 1, rest + arr[index], dp);
        }
        if (!dp.containsKey(index)) {
            dp.put(index, new HashMap<>());
        }
        dp.get(index).put(rest, ans);
        return ans;
    }

第三种方式动态规划(二维动态规划的空间压缩技巧)

理解业务过程

    // 优化点一 :
    // 你可以认为arr中都是非负数
    // 因为即便是arr中有负数,比如[3,-4,2]
    // 因为你能在每个数前面用+或者-号
    // 所以[3,-4,2]其实和[3,4,2]达成一样的效果
    // 那么我们就全把arr变成非负数,不会影响结果的
    // 优化点二 :
    // 如果arr都是非负数,并且所有数的累加和是sum
    // 那么如果target<sum,很明显没有任何方法可以达到target,可以直接返回0
    // 优化点三 :
    // arr内部的数组,不管怎么+和-,最终的结果都一定不会改变奇偶性
    // 所以,如果所有数的累加和是sum,
    // 并且与target的奇偶性不一样,没有任何方法可以达到target,可以直接返回0
    // 优化点四 :
    // 比如说给定一个数组, arr = [1, 2, 3, 4, 5] 并且 target = 3
    // 其中一个方案是 : +1 -2 +3 -4 +5 = 3
    // 该方案中取了正的集合为P = {1,3,5}
    // 该方案中取了负的集合为N = {2,4}
    // 所以任何一种方案,都一定有 sum(P) - sum(N) = target
    // 现在我们来处理一下这个等式,把左右两边都加上sum(P) + sum(N),那么就会变成如下:
    // sum(P) - sum(N) + sum(P) + sum(N) = target + sum(P) + sum(N)
    // 2 * sum(P) = target + 数组所有数的累加和
    // sum(P) = (target + 数组所有数的累加和) / 2
    // 也就是说,任何一个集合,只要累加和是(target + 数组所有数的累加和) / 2
    // 那么就一定对应一种target的方式
    // 也就是说,比如非负数组arr,target = 7, 而所有数累加和是11
    // 求有多少方法组成7,其实就是求有多少种达到累加和(7+11)/2=9的方法
    // 优化点五 :
    // 二维动态规划的空间压缩技巧
    public static int findTargetSumWays(int[] arr, int target) {
        int sum = 0;
        for (int n : arr) {
            sum += n;
        }
        return sum < target || ((target & 1) ^ (sum & 1)) != 0 ? 0 : subset2(arr, (target + sum) >> 1);
    }
    // 求非负数组nums有多少个子集,累加和是s
    // 二维动态规划
    // 不用空间压缩
    public static int subset1(int[] nums, int s) {
        if (s < 0) {
            return 0;
        }
        int n = nums.length;
        // dp[i][j] : nums前缀长度为i的所有子集,有多少累加和是j?
        int[][] dp = new int[n + 1][s + 1];
        // nums前缀长度为0的所有子集,有多少累加和是0?一个:空集
        dp[0][0] = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= s; j++) {
                dp[i][j] = dp[i - 1][j];
                if (j - nums[i - 1] >= 0) {
                    dp[i][j] += dp[i - 1][j - nums[i - 1]];
                }
            }
        }
        return dp[n][s];
    }

    // 不会空间压缩
    // 求非负数组nums有多少个子集,累加和是s
    // 二维动态规划
    // 用空间压缩:
    // 核心就是for循环里面的:for (int i = s; i >= n; i--) {
    // 为啥不枚举所有可能的累加和?只枚举 n...s 这些累加和?
    // 因为如果 i - n < 0,dp[i]怎么更新?和上一步的dp[i]一样!所以不用更新
    // 如果 i - n >= 0,dp[i]怎么更新?上一步的dp[i] + 上一步dp[i - n]的值,这才需要更新
    public static int subset2(int[] nums, int s) {
        if (s < 0) {
            return 0;
        }
        int[] dp = new int[s + 1];
        dp[0] = 1;
        for (int n : nums) {
            for (int i = s; i >= n; i--) {
                dp[i] += dp[i - n];
            }
        }
        return dp[s];
    }

题目4:现有司机M人,调度中心会将所有司机平分给A、B俩个区域。第i个司机去A可得到收入为income[i][0],第i个司机去B可得到收入为income[i][2],那么返回所有调度方案中能使所有司机总收入最高的方案,是多少钱。

    public static int maxMoney1(int[][] income) {
        if (income == null || income.length < 2 || (income.length & 1) != 0) {
            return 0;                  //(income.length & 1) != 0)  位运算,看其奇偶性
        }
        int N = income.length; // 司机数量一定是偶数,所以才能平分,A N /2 B N/2
        int M = N >> 1; // M = N / 2 要去A区域的人
        return process1(income, 0, M);
    }

    // index.....所有的司机,往A和B区域分配!
    // A区域还有rest个名额!
    // 返回把index...司机,分配完,并且最终A和B区域同样多的情况下,
    // index...这些司机,整体收入最大是多少!
    //    因为A区域能够推断出B区域。所以只需要一个区域变量就可以
    public static int process1(int[][] income, int index, int rest) {
        if (index == income.length) {
            return 0;
        }
        // 还剩下司机!
        if (income.length - index == rest) {
            return income[index][0] + process1(income, index + 1, rest - 1);
        }
        if (rest == 0) {
            return income[index][1] + process1(income, index + 1, rest);
        }
        // 当前司机,可以去A,或者去B
        int p1 = income[index][0] + process1(income, index + 1, rest - 1);
        int p2 = income[index][1] + process1(income, index + 1, rest);
        return Math.max(p1, p2);
    }

题目5:HashMap实现setAll的功能。

import java.util.HashMap;

public class Code05_SetAll {

    public static class MyValue<V> {
        public V value;
        public long time;

        public MyValue(V v, long t) {
            value = v;
            time = t;
        }
    }

    public static class MyHashMap<K, V> {
        private HashMap<K, MyValue<V>> map;
        private long time;  
        private MyValue<V> setAll;

        public MyHashMap() {
            map = new HashMap<>();
            time = 0;
            setAll = new MyValue<V>(null, -1);
        }

        public void put(K key, V value) {
            map.put(key, new MyValue<V>(value, time++));
        }

        public void setAll(V value) {
            setAll = new MyValue<V>(value, time++);
        }

        public V get(K key) {
            if (!map.containsKey(key)) {
                return null;
            }
            if (map.get(key).time > setAll.time) {
                return map.get(key).value;
            } else {
                return setAll.value;
            }
        }
    }
}

题目6:(leetcode3)求一个字符串中,最长无重复字符子串长度

暴力方法:俩个for循环即可实现。

我们是不是可以:每一个数字,都往左边推,去寻找目标S。取其MAX即可。

当子串、当子数组。。。。遇到这种问题的时候,就这么想~

从  0==》S    从 1 ==》 S 

那么从整体上看,是不是从左到右的计算过程。那么我i位置上的内容,是不是可以依赖于i-1得出。

所以,此时根据此思想,可以想到加速的办法。

1)当前字符上次的位置

2)i-1位置往左推的距离

取MAX即可。又因为,我们只需要得到上一次的内容,所有只需要变量记录即可!!!

public class Code06_LongestSubstringWithoutRepeatingCharacters {

    public static int lengthOfLongestSubstring(String s) {
        if (s == null || s.equals("")) {
            return 0;
        }
        char[] str = s.toCharArray();
        // ASCII 只有 0~255
        // map[i] = v  i这个ascii码的字符,上次出现在V位置
        int[] map = new int[256];
        for (int i = 0; i < 256; i++) {
            map[i] = -1;
        }
        map[str[0]] = 0;
        int N = str.length;
        int ans = 1;       // 最少长度 1
        int pre = 1;       // 上一个位置,向左推了多长
        for (int i = 1; i < N; i++) {
            pre = Math.min(i - map[str[i]], pre + 1);
            ans = Math.max(ans, pre);
            map[str[i]] = i;
        }
        return ans;
    }

}

题目7:给定一个数组arr,代表每个人的能力值。在给定一个非负数k,如果俩个人能力差值正好为k,那么可以凑在一起比赛。一局比赛只有俩个人,返回最多可以同时有多少场比赛

题意解析: [ 3 1 5 7 ]  k = 2   此时最多安排2场比赛  1 3  和 5 7

此时根据题意:可以想到一个暴力求解的方法,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值