LeetCode 热题 HOT 100 打卡计划 | 第二天 | 每日进步一点点

103 阅读3分钟

图片.png

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

4. 寻找两个正序数组的中位数 *

思路

(递归) O(log(n+m))

找出两个正序数组的中位数等价于找出两个正序数组中的第k小数。如果两个数组的大小分别为nm ,那么第 k = (n + m)/2 小数就是我们要求的中位数。

如何寻找第k小的元素?

过程如下:

1、考虑一般情况,我们在 nums1nums2数组中各取前k/2个元素

图片.png

我们默认nums1数组比nums2数组的有效长度小 。nums1数组的有效长度从i开始,nums2数组的有效长度从j开始,其中[i,si - 1]nums1数组的前k / 2个元素,[j, sj - 1]nums2数组的前k / 2个元素。

2、接下来我们去比较nums1[si - 1]nums2[sj - 1]的大小。

  • 如果nums1[si - 1] > nums2[sj - 1] ,则说明 nums1 中取的元素过多,nums2 中取的元素过少。因此nums2 中的前 k/2个元素一定都小于等于第 k 小数,即nums2[j,sj-1]中元素。我们可以舍去这部分元素,在剩下的区间内去找第k - k / 2小的元素,也就是说第k小一定在[i,n][sj,m]中。
  • 如果nums1[si - 1] <= nums2[sj - 1],同理可说明nums2中的前 k/2个元素一定都小于等于第 k 小数,即nums1[i,si-1]中元素。我们可以舍去这部分元素,在剩下的区间内去找第k - k / 2小的元素,也就是说第k小一定在[si,n][j,m]中。

3、递归过程2,每次可将问题的规模减少一半,最后剩下的一个数就是我们要找的第k小数。

递归边界:

  • nums1数组为空时,我们直接返回nums2数组的第k小数。
  • k == 1时,且两个数组均不为空,我们返回两个数组首元素的最小值,即min(nums1[i], nums2[j])

奇偶分析:

  • 当两个数组元素个数的总和total为偶数时,找到第total / 2left和第total / 2 + 1right,结果是(left + right / 2.0)
  • total为奇数时,找到第total / 2 + 1小,即为结果。

时间复杂度分析: k=(m+n)/2,且每次递归 k 的规模都减少一半,因此时间复杂度是O(log(m+n)).

c++代码

 class Solution {
 public:
     double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
         int tot = nums1.size() + nums2.size();
         if(tot % 2 == 0){
             int left = find(nums1, 0, nums2, 0, tot / 2);
             int right =find(nums1, 0, nums2, 0, tot / 2 + 1);
             return (left + right) / 2.0;
         }else{
             return find(nums1, 0, nums2, 0, tot / 2 + 1);
         }   
     }
 ​
     int find(vector<int>& nums1,int i, vector<int>& nums2, int j, int k){
         if(nums1.size() - i > nums2.size() - j) return find(nums2, j, nums1, i, k);
         if(k == 1){
             //当第一个数组已经用完
             if(i == nums1.size()) return nums2[j];
             else return min(nums1[i], nums2[j]);
         }
         //当nums1数组为空时,我们直接返回nums2数组的第k小数。
         if (nums1.size() == i) return nums2[j + k - 1];
         int si = min((int)nums1.size(), i + k / 2), sj = j + k - k / 2;
         if(nums1[si - 1] > nums2[sj - 1]){
             return find(nums1, i, nums2, sj, k - (sj - j));
         }else{
             return find(nums1, si, nums2, j, k - (si - i));
         }
     }
 };

10. 正则表达式匹配 *

思路

(动态规划) O(nm)

状态表示: f[i][j] 表示字符串s 的前 i个字符和字符串 p 的前j 个字符能否匹配。

状态计算:

根据p[j] 是什么来划分集合:

  • 1、p[j] != '*' ,即p[j]是字符, 看p[j]s[i]的关系。如果p[j] == s[i],则需判断 s的前i - 1个字母 能否和p的前j -1个字母匹配 ,即f[i][j] == f[i - 1][j - 1],不匹配 , 无法转移。

  • 2 P[j] 是匹配符:

    • 如果p[j] == '.',则p[j]s[j]匹配 ,则需判断 s的前i - 1个字母能否和p的前j -1个字母匹配 ,即f[i][j] == f[i - 1][j - 1]
    • p[j] == '*',得看p[j - 1]s[i]的关系。如果不匹配,即p[j - 1] != s[i],那么'*'匹配0p[j - 1],则需判断 s的前i个字母 能否和p的前j - 2个字母匹配 ,即f[i][j] == f[i][j - 2]。如果匹配,即p[j - 1] == s[i] || p[j - 1] == '.',则需判断s的前i - 1个字母能否和p的前j个字母匹配 ,即f[i][j] == f[i - 1][j])

图片.png 总结:

 f[i][j] == f[i - 1][j - 1], 前提条件为p[j] == s[i] || p[j] == '.'
 f[i][j] == f[i][j - 2], 前提条件为p[j] == '*' &&  p[j - 1] != s[i]
 f[i][j] == f[i - 1][j], 前提条件为p[j] == '*' && ( p[j - 1] == s[i] || p[j - 1] == '.'

c++代码

 class Solution {
 public:
     bool isMatch(string s, string p) {
         int n = s.size(), m = p.size();
         s = ' ' +  s, p = ' ' + p;
         vector<vector<bool>> f(n + 1, vector<bool>(m + 1));
         f[0][0] = true;
         for(int i = 0; i <= n; i++)
             for(int j = 1; j <= m; j++){
                 if(j + 1 <= m && p[j + 1] == '*') continue;
                 if(i &&p[j] != '*'){
                     f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
                 }else if(p[j] == '*'){
                     f[i][j] = f[i][j - 2] || i && f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '.');
                 }
             }
         return f[n][m];    
     }
 };

5. 最长回文子串

思路

(双指针) O(n^2)

  • 1、枚举数组中的每个位置i,从当前位置开始向两边扩散
  • 2、当回文子串的长度是奇数时,从i - 1,i + 1开始往两边扩散
  • 3、当回文子串的长度是偶数时,从ii + 1开始往两边扩散
  • 4、找到以i为中心的最长回文子串的长度,若存在回文子串比以前的长,则更新答案。

图示:

图片.png 时间复杂度分析: 枚举数组中的每个位置i需要O(n)的时间复杂度,求回文子串需要O(n)的时间复杂度,因此总的时间复杂度为O(n^2)。

c++代码

 class Solution {
 public:
     string longestPalindrome(string s) {
         string res;
         for(int i = 0; i < s.size(); i++){
             int l = i, r = i + 1;  //回文串长度为偶数
             while(l >= 0 && r < s.size() && s[l] == s[r]) l--, r++;
             if(res.size() < r - l - 1) res = s.substr(l + 1, r - l - 1);
             l = i - 1, r = i + 1;  //回文串长度为奇数
             while(l >= 0 && r < s.size() && s[l] == s[r]) l--, r++;
             if(res.size() < r - l - 1) res = s.substr(l + 1, r - l - 1);
         }
         return res;
     }
 };