leetcode笔记

1.求和问题

1. 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

思路:查找问题:target-nums[i],比如target = 9,那我们只需要找比如9-2=7;我们要看数组里面有没有7;判断数组里有没有,第一时间想到hashmap提供的containsKey()方法,将target-nums[i]作为键,索引为值

注意:集合中存放的是要找的数,一旦命中直接返回

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int res[] = new int[2];
        HashMap<Integer,Integer> hp = new HashMap();
        for(int i=0;i<nums.length;i++){
            if(hp.containsKey(nums[i])){//找到了直接返回
                res[0] = i;
                res[1] = hp.get(nums[i]);
                return res;
            }
            hp.put(target-nums[i],i);//没找到,加入hashmap
        }
        return res;
    }
}

2. 两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

leetcode笔记_第1张图片

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

思路:每位数字逆序排列,则第一位是个位,依此类推十位百位…从个位开始做加法,产生的进位加到下一位,使用循环,循环条件为两链表不为空,当循环结束进位不为0那么将进位加入链表

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode pre = new ListNode(0);//建立新链表头结点
        ListNode cur = pre;//将头结点赋给cur用来操作新链表
        int carry = 0;//进位
        while(l1 != null || l2 != null) {
            int x = l1 == null ? 0 : l1.val;
            int y = l2 == null ? 0 : l2.val;
            int sum = x + y + carry;
            
            carry = sum / 10;
            sum = sum % 10;
            cur.next = new ListNode(sum);

            cur = cur.next;
            if(l1 != null)
                l1 = l1.next;
            if(l2 != null)
                l2 = l2.next;
        }
        if(carry>0) {
            cur.next = new ListNode(carry);
        }
        return pre.next;
    }
}

3.数组中和为 0 的三个数

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a ,b ,c ,使得 a + b + c = 0 ?请找出所有和为 0 且 不重复 的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:

输入:nums = []
输出:[]

思路:做完了两数之和又来了三数之和,思路和两数差不多,先进行排序,然后固定一个数,然后找两个数和为该固定数的相反数

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
            if(nums == null||nums.length<3)return new  ArrayList();
            Arrays.sort(nums);
            Set<List<Integer>> res = new HashSet<>();
            for(int i=0;i<nums.length;i++){
                int temp = -nums[i];
                int left = i+1;
                int right = nums.length-1;
                while(left<right){
                    if(nums[left]+nums[right]==temp){
                        res.add(Arrays.asList(nums[i],nums[left],nums[right]));
                        left++;
                        right--;
                    }else if(nums[left]+nums[right]<temp){
                        left++;
                    }else{
                         right--;
                    }
                }
            }
            return new ArrayList(res);
    }
}

2.字符串问题

1. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

思路:一切尽在代码中

class Solution {
     public int lengthOfLongestSubstring(String s) {
         //定义左右指针,初始时指向同一位置,并定义最大长度,和当前长度
         int left=0,right=0,len=0,maxlen=0;
         HashMap<Character,Integer> hp = new HashMap();//使用map来存储字符串
         for(right=0;right<s.length();right++){//遍历字符串
             char c = s.charAt(right);
             if(!hp.containsKey(c)){//如果集合中没有该字符,加进去,并将len加1
                 hp.put(c,right);
                 len++;
                 if(len>maxlen){
                     maxlen=len;
                 }
                 //如果集合中有,那么右移左指针,并删除对应元素,直到可以让右指针所指元素加进去
                 hp.remove(s.charAt(left));
                 left++;
                 len--;//删除元素后长度--
             }
         }
         return maxlen;
     }
}

3.位运算

1. 整数除法

给定两个整数 a 和 b ,求它们的除法的商 a/b ,要求不得使用乘号 ‘*’、除号 ‘/’ 以及求余符号 ‘%’ 。

注意:

整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231−1]。本题中,如果除法结果溢出,则返回 231 − 1

示例 1:

输入:a = 15, b = 2
输出:7
解释:15/2 = truncate(7.5) = 7
示例 2:

输入:a = 7, b = -3
输出:-2
解释:7/-3 = truncate(-2.33333..) = -2

思路:该题可采用做减法,注意边界问题:

a=7,b=2;

让a每次减去b直到a

  • 7-2=5 5>2

  • 5-2=3 3>2

  • 3-2=1 1<2

    这样统计做了几次减法即可(正数)

    优化:减倍数

    每次减去b的倍数

    7-2=5 减掉1个2 k=1

    7-(2+2)=3 k=k+k=2 每次等于上一轮的k相加

    ,7-((2+2)*2)<0,那么将a变成3,3-2=1<2 k=1

    再优化:位运算

    a=22,b=3

    22-3<<0=19>0 k=1<<0=1

    22-3<<1=16>0 k=1<<2=4

    22-3<<2=10>0 k=1<<3=8

    22-3<<4<0

    将a换成10

    10-3<<0=7>0 k=1<<0=1

    10-3<<1=4>0 k=1<<1=2

    10-3<<2<0

    换成4

    4-3<<0=1>0 k=1<<0

    4-3<<1<0

    再优化:每次从最大位32位尝试,这样循环次数32次,时间复杂度达到o(1)

    22-3<<31

    22-3<<32

class Solution {
    public int divide(int a, int b) {
        //32位最大值2147483648
        //32位最小值-2147483648
        //-2147483648/-1=2147483648越界了
        if(a==Integer.MIN_VALUE&&b==-1){
            return Integer.MAX_VALUE;
        }
        //将a,b取为正数
       int a1=a<0?-a:a;
       int b1=b<0?-b:b;//最小值-2147483648不会改变,下面采用<<<
        int res=0;
    for(int i=31;i>=0;i--){
        if((a1>>>i)-b1>=0){//如果这里使用a>=b<
            a1-=b1<<i;
            res=res+(1<<i);
        }
    }
    if((a>0&&b<0)||(a<0&&b>0)){//如果a,b中有一个为负数
             return -res;
        }else{
            return res;
        }
    }
}

2.二进制加法

给定两个 01 字符串 a 和 b ,请计算它们的和,并以二进制字符串的形式输出。

输入为 非空 字符串且只包含数字 1 和 0。

示例 1:

输入: a = "11", b = "10"
输出: "101"
示例 2:

输入: a = "1010", b = "1011"
输出: "10101"

题解:该题和上面链表相加很像,对位相加后可能会产生进位

class Solution {
    public String addBinary(String a, String b) {
        StringBuilder sb=new StringBuilder();
        int i=a.length()-1,j=b.length()-1,c=0;
        while(i>=0||j>=0||c!=0){
            int ii=i>=0?a.charAt(i--)-'0':0;
            int jj=j>=0?b.charAt(j--)-'0':0;
            c=ii+jj+c;
            sb.append(c%2);
            c/=2;
        }
        return sb.reverse().toString();
    }
}

3.前 n 个数字二进制中 1 的个数

给定一个非负整数 n ,请计算 0 到 n 之间的每个数字的二进制表示中 1 的个数,并输出一个数组。

示例 1:

输入: n = 2
输出: [0,1,1]
解释: 
0 --> 0
1 --> 1
2 --> 10
示例 2:

输入: n = 5
输出: [0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101


思路:对于所有的数字,只有奇数和偶数两种:

  • 奇数:二进制表示中,奇数一定比前面那个偶数多一个 1,因为多的就是最低位的 1

    例如:5->101,4->100
    
  • 偶数:二进制表示中,偶数中 1 的个数一定和除以 2 之后的那个数一样多。因为最低位是 0,除以 2 就是右移一位,也就是把那个 0 抹掉而已,所以 1 的个数是不变的。

    12->1100,6->110,6=12>>1
    

所以该题可使用位运算+动态规划

dp[i]=dp[i-1]i为奇数
dp[i]=dp[i/2]i为偶数

对上面方程做出优化:

dp[i]=dp[i/2]+i%2=>dp[i]=dp[i>>1]+i%2
class Solution {
    public int[] countBits(int n) {
        int[] res = new int[n+1];
        res[0] = 0;
        for(int i = 1; i <= n; i++){
            res[i] = res[i>>1] + i % 2;
        }
        return res;
    }
}

4.只出现一次的数字

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

示例 1:

输入:nums = [2,2,3,2]
输出:3
示例 2:

输入:nums = [0,1,0,1,0,1,100]
输出:100

思路:这种问题第一时间想到的大概就是hash表了,将其放入hash表记录其出现的次数

class Solution {
    public int singleNumber(int[] nums) {
        HashMap<Integer,Integer> map = new HashMap<>();
        for(int i = 0;i<nums.length;i++){
            map.put(nums[i],map.getOrDefault(nums[i],0)+1);
        }
        for(Map.Entry<Integer,Integer> entry:map.entrySet()){
            if(entry.getValue() == 1)
                return entry.getKey();
        }
        return 0;
    }
}

使用位运算:

class Solution {
    //将整数的各个数位上的加起来,然后对3取余,若结果为0,则待求数字在该位上是0;
    //若结果为1,则待求数字在该位上是1.
    public int singleNumber(int[] nums) {
        //java的int整型为32位
        int[] arr=new int[32];
        for(int num:nums){
            for(int i=0;i<32;i++){
                arr[i]+=(num>>(31-i))&1;
            }
        }
        int res=0;
        for(int i=0;i<32;i++){
            res=(res<<1)+arr[i]%3;
        }
        return res;
    }
}

5.单词长度的最大乘积

给定一个字符串数组 words,请计算当两个字符串 words[i] 和 words[j] 不包含相同字符时,它们长度的乘积的最大值。假设字符串中只包含英语的小写字母。如果没有不包含相同字符的一对字符串,返回 0。

示例 1:

输入: words = ["abcw","baz","foo","bar","fxyz","abcdef"]
输出: 16 
解释: 这两个单词为 "abcw", "fxyz"。它们不包含相同字符,且长度的乘积最大。
示例 2:

输入: words = ["a","ab","abc","d","cd","bcd","abcd"]
输出: 4 
解释: 这两个单词为 "ab", "cd"。

思路:该题判断单词相同可使用位运算;

int ->32位,总共有26个字母

int 一共32位我们使用26位从右开始依次向左代表a-z,字母存在该位为1不存在为0

相关知识:

  • 与:一假则假,全真才真
  • 或:一真则真,全假则假
class Solution {
    public int maxProduct(String[] words) {
	//先将每个单词转化位bit数组
        int len = words.length;
        int masks[] = new int[len];
        for(int i=0;i<len;i++){
            int bit = 0;//定义一个bit32位
            for(char c : word[i].toCharArray()){
                bit = bit | (1<<(c - 'a'));
            }
            masks[i] = bit;
        }
        
        int ans = 0;//最大结果
        for(i = 0;i<len;i++){
            String word1 = word[i].toCharArray();
            for(j=j+1;j<len;j++){
                if(masks[i] & masks[j] == 0){//相与为零说明不同
                    ans = Math.max(ans,word[i].length*word[j].length);
                }
            }
            return ans;
        }
    }
}

4.双指针

1. 两数之和(优化)

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

思路:对这道题进行优化以下

这次使用双指针,左指针从索引为0处开始,右指针从索引最大处开始

如果numbers[left]+numbers[right] < target;说明得让最小值变大一点;反之一样

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int left = 0,right = numbers.length-1;
        int i = 0;
       while(left<right){
           if(numbers[left]+numbers[right] == target){//找到了,跳出循环
               break;
           }else if(numbers[left]+numbers[right] < target){
               left++;
           }else{
               right--;
           }
       }
       return new int[]{left,right};
    }
}

2.和大于等于 target 的最短子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

思路:利用双指针start ,end,分别指向子数组的头和尾,使用临时变量sum统计他们的和

class Solution {
    //双指针法,类似于滑动窗口
    public int minSubArrayLen(int target, int[] nums) {
        //设置初始最小值
        int min=Integer.MAX_VALUE;
        //初始化数组和
        int sum=0;
        //定义两个变量i、j(类似于滑动窗口的感觉)
        for(int i=0,j=0;j<nums.length;j++){
            //扩大窗口
            sum+=nums[j];
            while(i<=j && sum>=target){
                //更新最小值
                min=Math.min(min,j-i+1);
                //缩小窗口
                sum-=nums[i++];
            }
        }
        //若所有数组和都小于target,则返回0,否则返回更新值
        return min==Integer.MAX_VALUE?0:min;
    }
}

3.乘积小于 K 的子数组

给定一个正整数数组 nums和整数 k ,请找出该数组内乘积小于 k 的连续的子数组的个数。

示例 1:

输入: nums = [10,5,2,6], k = 100
输出: 8
解释: 8 个乘积小于 100 的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于100的子数组。

思路: /**
* 思路: 设存在数组nums=[A, B, C, D], k为乘积, count为符合条件的数组个数, i,j为窗口左右边界;
*(假设) A: A count = A (0-0+1)
* B: AB count = AB, B(1-0+1)
* C: ABC>=k j=2 --> BC count = BC, C (2-1+1)
* D: BCD>k j=3 --> CD>K i=2 --> D < k i=3 --> count = D (3-3+1)
* 当计算的数组乘积大于k时,将窗口左边界右移, 直到小于k, 计算count,窗口右边界右移;
* 当计算的数组乘积小于k时,计算count,窗口右边界右移
* 得出规律:每一次遍历count增加了j-i+1
*/

class Solution {
 
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        if(nums == null || k < 1){
            return 0;
        }
        int i = 0, j = 0;
        int res = 1, count = 0;
        while(j < nums.length){
            res *= nums[j];
            while(i <= j && res >= k){
                res /= nums[i++];
            }
            if(i <= j){
                count += j - i + 1;
            }
            j++;
        }

        return count;
    }
}

5.贪心

1. 分发饼干

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

示例 1:

输入: g = [1,2,3], s = [1,1]
输出: 1
解释: 
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。

思路:把他们按照从小到大排序,然后遍历两个数组,如果 s[j] >= g[i],那给加过加1

class Solution {
    public int findContentChildren(int[] g, int[] s) {
		Arrays.sort(g);
        Arrays.sort(s);
        int res = 0,temp = 0;
        while(res < g.length&&temp < s.length){
            if(s[temp]>=g[res])res++;
            temp++;
        }
    }
}

2.种花问题

假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。

给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false。

示例 1:

输入:flowerbed = [1,0,0,0,1], n = 1
输出:true

思路:经典跳格子:遍历如果遍历完了,或者n为0了结束,有三种情况

  • 当前各自是1,那么说明不能种且下一各自也不行,跳两格
  • 当前格子是0,判断下一个各自是不是1,如果是说明不能种,且后两格都不行,跳3格
  • 如果跳到最后一格,或者说当前格子是0那么n–
class Solution {
    public boolean canPlaceFlowers(int[] flowerbed, int n) {
        for(int i=0,len=flowerbed.length;i<len&&n>0;){
            if(flowerbed[i]==1){
                i=i+2;
            }else if(i==len-1||flowerbed[i+1]==0){
                i=i+2;
                n--;
            }else{
                i=i+3;
            }
        }
        return n<=0;
    }
}

6.子序列问题

1. 最长递增子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

思路:动态规划

dp[i]表示i之前包括以nums[i]结尾的最长上升子序列长度

转移方程:

当nums[i]>nums[j]时,dp[i]=max(dp[i],dp[j]+1)

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

2.最长连续递增序列

给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。

连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], …, nums[r - 1], nums[r]] 就是连续递增子序列。

示例 1:

输入:nums = [1,3,5,4,7]
输出:3
解释:最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。


思路:

本题与上题不同之处在于连续

dp[i]表示以下标i为结尾的数组的连续递增的子序列长度为dp[i]

由于是连续的与上题不同这次不一定是以下标0开始

转移方程:

当nums[i]>nums[i-1]时,dp[i]=dp[i-1]+1;

class Solution {
    public int findLengthOfLCIS(int[] nums) {
        if(nums.length<=1)return nums.length;
        int dp[]=new int[nums.length];
        int res=0;
        Arrays.fill(dp,1);//初始化数组长度为1,因为长度最短也为1
    for(int i=1;i<dp.length;i++){
        if(nums[i]>nums[i-1]){
            dp[i]=dp[i-1]+1;
        }
        if(dp[i]>res){
            res=dp[i];
        }
    }
    return res;
    }
}

当然本题也可使用贪心

class Solution {
    public int findLengthOfLCIS(int[] nums) {
        if(nums.length<=1)return nums.length;
     int res=1;
     int count=1;
     for(int i=1;i<nums.length;i++){
         //如果nums[i]>nums[i-1]那么给count+1
         if(nums[i]>nums[i-1]){
             count++;
         }else{
             //否则小于或者不连续将count置回1
             count=1;
         }
         //每次更新res
         if(res<count){
             res=count;
         }
     }
     return res;
    }
}

概括来说:不连续递增子序列的跟前0-i 个状态有关,连续递增的子序列只跟前一个状态有关

3.最长重复子数组

给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 。

示例 1:

输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
输出:3
解释:长度最长的公共子数组是 [3,2,1] 。

思路:dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。 (特别注意: “以下标i - 1为结尾的A” 标明一定是 以A[i-1]为结尾的字符串 )

  1. 确定递推公式

根据dp[i][j]的定义,dp[i][j]的状态只能由dp[i - 1][j - 1]推导出来。

即当A[i - 1] 和B[j - 1]相等的时候,dp[i][j] = dp[i - 1][j - 1] + 1;

根据递推公式可以看出,遍历i 和 j 要从1开始!

class Solution {
    public int findLength(int[] nums1, int[] nums2) {
        int res=0;
        int dp[][]=new int[nums1.length+1][nums2.length+1];
        for(int i=1;i<nums1.length+1;i++)
        for(int j=1;j<nums2.length+1;j++){
            if(nums1[i-1]==nums2[j-1]){
                dp[i][j]=dp[i-1][j-1]+1;
                res=Math.max(res,dp[i][j]);
            }
            
        }
        return res;
    }
}

4.最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。


思路:动态规划

将其分解为几个子问题:

子问题 1:以 -2−2 结尾的连续子数组的最大和是多少;
子问题 2:以 11 结尾的连续子数组的最大和是多少;
子问题 3:以 -3−3 结尾的连续子数组的最大和是多少;
子问题 4:以 44 结尾的连续子数组的最大和是多少;
子问题 5:以 -1−1 结尾的连续子数组的最大和是多少;
子问题 6:以 22 结尾的连续子数组的最大和是多少;
子问题 7:以 11 结尾的连续子数组的最大和是多少;
子问题 8:以 -5−5 结尾的连续子数组的最大和是多少;
子问题 9:以 44 结尾的连续子数组的最大和是多少。

可见问题1和问题2就是:当dp[i-1]>0时dp[i]=dp[i-1]+nums[i]

或者:dp[i]=Math.max(dp[i-1]+nums,nums)

class Solution {
    public int maxSubArray(int[] nums) {
        
        int res=nums[0];
        //dp[i]表示前i个元素的最大和
        int dp[]=new int[nums.length];
        dp[0]=nums[0];
        for(int i=1;i<nums.length;i++){
            dp[i]=Math.max(nums[i],dp[i-1]+nums[i]);
            res=Math.max(res,dp[i]);
        }
        return res;
    }
}

5.最长公共子序列

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 。


  1. 确定dp数组(dp table)以及下标的含义

dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j]

有同学会问:为什么要定义长度为[0, i - 1]的字符串text1,定义为长度为[0, i]的字符串text1不香么?

这样定义是为了后面代码实现方便,如果非要定义为为长度为[0, i]的字符串text1也可以,大家可以试一试!

  1. 确定递推公式

主要就是两大情况: text1[i - 1] 与 text2[j - 1]相同,text1[i - 1] 与 text2[j - 1]不相同

如果text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1;

如果text1[i - 1] 与 text2[j - 1]不相同,那就看看text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列,取最大的。

即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);

class Solution {
    public int maxUncrossedLines(int[] nums1, int[] nums2) {
        int m=nums1.length,n=nums2.length;
        int dp[][]=new int[m+1][n+1];
        for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++){
            if(nums1[i-1]==nums2[j-1]){
                dp[i][j]=dp[i-1][j-1]+1;

            }else{
                dp[i][j]=Math.max(dp[i][j-1],dp[i-1][j]);
            }
        }
        return dp[m][n];
    }
}

6.不相交的线

在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。

现在,可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线,这些直线需要同时满足满足:

nums1[i] == nums2[j]
且绘制的直线不与任何其他连线(非水平线)相交。
请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。

以这种方法绘制线条,并返回可以绘制的最大连线数。

示例 1:

leetcode笔记_第2张图片

输入:nums1 = [1,4,2], nums2 = [1,2,4]
输出:2
解释:可以画出两条不交叉的线,如上图所示。 
但无法画出第三条不相交的直线,因为从 nums1[1]=4 到 nums2[2]=4 的直线将与从 nums1[2]=2 到 nums2[1]=2 的直线相交。


class Solution {
    public int maxUncrossedLines(int[] nums1, int[] nums2) {
        int m=nums1.length,n=nums2.length;
        int dp[][]=new int[m+1][n+1];
        for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++){
            if(nums1[i-1]==nums2[j-1]){
                dp[i][j]=dp[i-1][j-1]+1;

            }else{
                dp[i][j]=Math.max(dp[i][j-1],dp[i-1][j]);
            }
        }
        return dp[m][n];
    }
}

其实也就是说A和B的最长公共子序列是[1,4],长度为2。 这个公共子序列指的是相对顺序不变(即数字4在字符串A中数字1的后面,那么数字4也应该在字符串B数字1的后面)

这么分析完之后,大家可以发现:本题说是求绘制的最大连线数,其实就是求两个字符串的最长公共子序列的长度!

那么本题就和我们刚刚讲过的这道题目动态规划:1143.最长公共子序列 (opens new window)就是一样一样的了。

== nums2[j]
且绘制的直线不与任何其他连线(非水平线)相交。
请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。

以这种方法绘制线条,并返回可以绘制的最大连线数。

示例 1:

[外链图片转存中…(img-xZSxVO5r-1660880847273)]

输入:nums1 = [1,4,2], nums2 = [1,2,4]
输出:2
解释:可以画出两条不交叉的线,如上图所示。 
但无法画出第三条不相交的直线,因为从 nums1[1]=4 到 nums2[2]=4 的直线将与从 nums1[2]=2 到 nums2[1]=2 的直线相交。


class Solution {
    public int maxUncrossedLines(int[] nums1, int[] nums2) {
        int m=nums1.length,n=nums2.length;
        int dp[][]=new int[m+1][n+1];
        for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++){
            if(nums1[i-1]==nums2[j-1]){
                dp[i][j]=dp[i-1][j-1]+1;

            }else{
                dp[i][j]=Math.max(dp[i][j-1],dp[i-1][j]);
            }
        }
        return dp[m][n];
    }
}

其实也就是说A和B的最长公共子序列是[1,4],长度为2。 这个公共子序列指的是相对顺序不变(即数字4在字符串A中数字1的后面,那么数字4也应该在字符串B数字1的后面)

这么分析完之后,大家可以发现:本题说是求绘制的最大连线数,其实就是求两个字符串的最长公共子序列的长度!

那么本题就和我们刚刚讲过的这道题目动态规划:1143.最长公共子序列 (opens new window)就是一样一样的了。

一样到什么程度呢? 把字符串名字改一下,其他代码都不用改,直接copy过来就行了。

你可能感兴趣的:(leetcode,算法,数据结构)