数据结构与算法之字典(五)

1,417 阅读2分钟

1、LeetCode: 349.两个数组的交集

解题思路:

  • 求 nums1 和 nums2 都有的值
  • 用字典建立一个映射关系,记录 nums1 里有的值
  • 遍历 nums2,找出 nums1 里也有的值

解题步骤:

  • 新建一个字典,遍历 nums1,填充字典
  • 遍历 nums2,遇到字典里的值就选出,并从字典中删除
// 时间复杂度 O(m + n) m为nums1长度 n为nums2长度
// 空间复杂度 O(m) m为交集的数组长度
const intersection = function(nums1, nums2) {
    const map = new Map();
    nums1.forEach(n1 => {
        map.set(n1, true);
    })
    const result = [];
    nums2.forEach(n2 => {
        if(map.get(n2)) {
            result.push(n2);
            map.delete(n2);
        }
    })
    return result;
};

2、LeetCode: 20.有效的括号

const isValid = s => {
  // 不是偶数,直接返回
  if (s.length % 2 !== 0) return false;
  const stack = [];
  const map = new Map();
  map.set("(", ")");
  map.set("{", "}");
  map.set("[", "]");
  for (let i = 0; i < s.length; i++) {
    const item = s[i];
    if (map.has(item)) {
      stack.push(item);
    } else {
      const top = stack[stack.length - 1];
      if (map.get(top) === item) {
        stack.pop();
      } else {
        return false;
      }
    }
  }
  return stack.length === 0;
};

3、LeetCode: 1. 两数之和

解题思路:

  • 把 nums 想象成相亲者
  • 把 target 想象成匹配条件
  • 用字典建立一个婚姻介绍所,存储相亲者的数字和下标

解题步骤:

  • 新建—个字典作为婚姻介绍所
  • 遍历 nums,让 nums 里的值,逐个来介绍所找对象,没有合适的就先登记着,有合适的就牵手成功
// 时间复杂度O(n) n为nums的length
// 空间复杂度O(n) map对象存储的值
const twoSum = function(nums, target) {
    const map = new Map();
    for(let i = 0; i < nums.length; i++) {
        const item = nums[i];
        const targetNumber = target - item;
        if(map.has(targetNumber)) {
            return [map.get(targetNumber), i];
        }else {
            map.set(item, i);
        }
    }
};

4、LeetCode: 3.无重复字符的最长子串

解题思路:

  • 先找出所有的不包含重复字符的子串
  • 找出长度最大那个子串,返回其长度即可

解题步骤:

  • 用双指针维护一个滑动窗口,用来剪切子串
  • 不断移动右指针,遇到重复字符,就把左指针移动到重复字符的下一位
  • 过程中,记录所有窗口的长度,并返回最大值
// 时间复杂度 O(n) n为s字符串长度
// 空间复杂度 O(m) 字典的长度,长度为字符串里不重复字符的个数
const lengthOfLongestSubstring = function(s) {
    // 左指针
    let l = 0;
    const map = new Map();
    let res = 0;
    for(let r = 0; r < s.length; r++) {
       // 判断字典里是否有重复字符 且 该字符必须在滑动窗口内
       if(map.has(s[r]) && map.get(s[r]) >= l) {
           l = map.get(s[r]) + 1;
       }
       // 记录每个窗口最大值
       res = Math.max(res, r - l + 1);
       map.set(s[r], r);
    }
    return res;
};

5、LeetCode: 76.最小覆盖子串

解题思路:

  • 先找出所有的包含T的子串
  • 找出长度最小那个子串,返回即可

解题步骤:

  • 用双指针维护一个滑动窗口
  • 移动右指针,找到包含T的子串,移动左指针,尽量减少包含T的子串的长度
  • 循环上述过程,找出包含T的最小子串
// 输入:s = "ADOBECODEBANC", t = "ABC"
// 输出:"BANC"

// 时间复杂度 O(m + n) m是t的长度 n是s的长度
// 空间复杂度 O(k) k是字符串中不重复字符的个数
const minWindow = function(s, t) {
    // 定义双指针维护一个滑动窗口
    let l = 0;
    let r = 0;
    const map = new Map();
    // 遍历t字符串,生成需要查找字符串map
    for(let key of t) {
        map.set(key, map.has(key) ? map.get(key) + 1 : 1);
    }
    let needType = map.size;
    // // 记录最小子串
    let res = "";
    // 移动右指针
    while(r < s.length) {
        const item = s[r];
        // 查找到遍历的值,减少其需要的数量
        if(map.has(item)) {
            map.set(item, map.get(item) - 1);
            if(map.get(item) === 0) needType -= 1;
        }
        // 当查找到了所有需要的值needType为0,就说明找到了一个子串(不一定是最小子串)
        while(needType === 0) {
            // 取出符合要求的最新子串
            const newRes = s.substring(l, r + 1);
            // 如果结果子串更大,则替换成当前最新的子串
            if(!res || res.length > newRes.length) {
                res = newRes;
            }
            // 从左指针处获取值
            const leftItem = s[l];
            // 如果字典里有值,而下面左指针会+1后,就会增加需要的值
            if(map.has(leftItem)) {
                map.set(leftItem, map.get(leftItem) + 1);
                if(map.get(leftItem) === 1) needType += 1;
            }
            l += 1;
        }
        r += 1;
    }
    return res;
};