力扣常见面试150题:哈希表+区间(java + c++版,只有简单和中等难度)

1.赎金信(383)

给你两个字符串:ransomNotemagazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false

magazine 中的每个字符只能在 ransomNote 中使用一次。

示例 1:

输入:ransomNote = "a", magazine = "b"
输出:false

示例 2:

输入:ransomNote = "aa", magazine = "ab"
输出:false

示例 3:

输入:ransomNote = "aa", magazine = "aab"
输出:true

提示:

  • 1 <= ransomNote.length, magazine.length <= 105
  • ransomNotemagazine 由小写英文字母组成
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        vector<int> m(26);
        for(int i = 0; i < magazine.length(); i++){
            m[magazine[i] - 'a']++;//统计magazine里面所有字符出现的次数
        }
        for(int i = 0; i < ransomNote.length(); i++){
            if(m[ransomNote[i] - 'a'] > 0){
                m[ransomNote[i] - 'a']--;//在原字符串中找到相应字符:-1
            }
            else{
                return false;//原字符串中找不到对应字符,就返回false
            }
        }
        return true;
    }
};
class Solution {//java版
    public boolean canConstruct(String ransomNote, String magazine) {
        //记录杂志字符串出现的次数
        int[] arr = new int[26];
        int temp;
        for (int i = 0; i < magazine.length(); i++) {
            temp = magazine.charAt(i) - 'a';
            arr[temp]++;
        }
        for (int i = 0; i < ransomNote.length(); i++) {
            temp = ransomNote.charAt(i) - 'a';
            //对于金信中的每一个字符都在数组中查找
            //找到相应位减一,否则找不到返回false
            if (arr[temp] > 0) {
                arr[temp]--;
            } else {
                return false;
            }
        }
        return true;
    }
}

2.同构字符串(205)

给定两个字符串 st ,判断它们是否是同构的。

如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。

每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序。不同字符不能映射到同一个字符上,相同字符只能映射到同一个字符上,字符可以映射到自己本身。

示例 1:

输入:s = "egg", t = "add"
输出:true

示例 2:

输入:s = "foo", t = "bar"
输出:false

示例 3:

输入:s = "paper", t = "title"
输出:true

提示:

  • 1 <= s.length <= 5 * 104
  • t.length == s.length
  • st 由任意有效的 ASCII 字符组成
class Solution {
    public :
    bool isIsomorphic(string s, string t) {
        if(s.length() != t.length()){
            return false;
        }
        unordered_map<char, char> ss;
        unordered_map<char, char> tt;//同构字符串需要两个字符串一一对应
        for(int i = 0; i < s.length(); i++){
            char x = s[i];
            char y = t[i];//遍历,用一个字符指代字符串的某个字符
            //如果映射里面没找到该字符且遍历字符不为另一个字符串的对应字符,就不是
            if((ss.count(x) && ss[x] != y) || (tt.count(y) && tt[y] != x)){
                return false;
            }
            ss[x] = y;
            tt[y] = x; 
        }
        return true;
    }
};
class Solution//java1
{
    public boolean isIsomorphic(String s, String t)
    {
        int len1 = s.length(), len2 = t.length();
        if (len1 != len2) return false;
        var hashmap = new HashMap<Character, Character>();
        for (int i = 0; i < len1; i++)
        {
            char c1 = s.charAt(i), c2 = t.charAt(i);
            if (hashmap.containsKey(c1) && hashmap.get(c1) != c2 || !(hashmap.containsKey(c1)) && hashmap.containsValue(c2))
                return false;
            hashmap.put(c1, c2);
        }
        return true;
    }
}
class Solution {//java2
    public boolean isIsomorphic(String s, String t) {
        Map<Character, Character> s2t = new HashMap<Character, Character>();
        Map<Character, Character> t2s = new HashMap<Character, Character>();
        int len = s.length();
        for (int i = 0; i < len; ++i) {
            char x = s.charAt(i), y = t.charAt(i);
            if ((s2t.containsKey(x) && s2t.get(x) != y) || (t2s.containsKey(y) && t2s.get(y) != x)) {
                return false;
            }
            s2t.put(x, y);
            t2s.put(y, x);
        }
        return true;
    }
}

3.单词规律(290)

给定一种规律 pattern 和一个字符串 s ,判断 s 是否遵循相同的规律。

这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 s 中的每个非空单词之间存在着双向连接的对应规律。

示例1:

输入: pattern = "abba", s = "dog cat cat dog"
输出: true

示例 2:

输入:pattern = "abba", s = "dog cat cat fish"
输出: false

示例 3:

输入: pattern = "aaaa", s = "dog cat cat dog"
输出: false

提示:

  • 1 <= pattern.length <= 300
  • pattern 只包含小写英文字母
  • 1 <= s.length <= 3000
  • s 只包含小写英文字母和 ' '
  • s 不包含 任何前导或尾随对空格
  • s 中每个单词都被 单个空格 分隔
class Solution {
public:
    bool wordPattern(string pattern, string s) {
        vector<string> a;
        int b = 0;
        stringstream ss;
        ss << s;//stringstream ss(s);也对
        string tmp;
        while(ss >> tmp){
            a.push_back(tmp);
        }
        unordered_map<char, string> m;
        unordered_map<string, char> n;
        if(a.size() != pattern.length()){
            return false;
        }
        for(int i = 0; i < pattern.length(); i++){
            char x = pattern[i];
            string y = a[i];
            if((m.count(x) && m[x] != y) || (n.count(y) && n[y] != x)){
                return false;
            }
            m[x] = y;
            n[y] = x;
        }
        return true;
    }
};
class Solution {//java
    public boolean wordPattern(String pattern, String s) {
        Map<Character, String> m = new HashMap<Character, String>();
        String[] a = s.split(" ");
        if(a.length != pattern.length()){
            return false;
        }
        for(int i = 0; i < pattern.length(); i++){
            char x = pattern.charAt(i);
            String y = a[i];
            if(!m.containsKey(x)){//映射里面没有键x
                if(!m.containsValue(y)){//映射里面也没值y,输入到映射
                    m.put(x, y);
                }
                else{
                    return false;//没有键x,还有值y,则不对应,输出false
                }
            }
            else{
                if(!m.get(x).equals(y)){//如果映射的键有x,但是和值y不一样就返回false
                    return false;
                }
            }
        }
        return true;
    }
}

4.有效的字母异位词(242)

给定两个字符串 *s**t* ,编写一个函数来判断 *t* 是否是 *s* 的字母异位词。

**注意:**若 *s**t* 中每个字符出现的次数都相同,则称 *s**t* 互为字母异位词。

示例 1:

输入: s = "anagram", t = "nagaram"
输出: true

示例 2:

输入: s = "rat", t = "car"
输出: false

提示:

  • 1 <= s.length, t.length <= 5 * 104
  • st 仅包含小写字母
class Solution {
public:
    bool isAnagram(string s, string t) {
        unordered_map<char, int> hash;
        if(s.length() != t.length()){
            return false;
        }
        for(int i = 0; i < s.length(); i++){
            hash[s[i]]++;
        }
        for(int i = 0; i < t.length(); i++){
            hash[t[i]]--;
            if(hash[t[i]] < 0){
                return false;
            }
        }
        return true;
    }
};
class Solution {//java
    public boolean isAnagram(String s, String t) {
        Map<Character, Integer> hash = new HashMap<Character, Integer>();
        if(s.length() != t.length()){
            return false;
        }
        for(int i = 0; i < s.length(); i++){
            hash.put(s.charAt(i), hash.getOrDefault(s.charAt(i), 0) + 1);
        }
        for(int i = 0; i < t.length(); i++){
            char ch = t.charAt(i);
            hash.put(ch, hash.getOrDefault(ch, 0) - 1);
            if(hash.get(ch) < 0){
                return false;
            }
        }
        return true;
    }
}

5.字母异位词分组(49)

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

示例 2:

输入: strs = [""]
输出: [[""]]

示例 3:

输入: strs = ["a"]
输出: [["a"]]

提示:

  • 1 <= strs.length <= 104
  • 0 <= strs[i].length <= 100
  • strs[i] 仅包含小写字母
法一:排序
class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string, vector<string>> map;//排序后的字符串对应的一组字符串
        for(int i = 0; i < strs.size(); i++){
            string s = strs[i];
            sort(s.begin(), s.end());
            map[s].push_back(strs[i]);//哈希表插入对应的字符串
        }
        vector<vector<string>> res;
        for(auto it = map.begin(); it != map.end(); it++){
            res.push_back(it->second);//插入哈希表的值
        }
        return res;
    }
};
class Solution {//java版
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<String, List<String>>();
        for (String str : strs) {
            char[] array = str.toCharArray();
            Arrays.sort(array);
            String key = new String(array);
            List<String> list = map.getOrDefault(key, new ArrayList<String>());
            list.add(str);
            map.put(key, list);
        }
        return new ArrayList<List<String>>(map.values());
    }
}
法二:计数
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<String, List<String>> map = new HashMap<String, List<String>>();
        for (String str : strs) {
            int[] counts = new int[26];
            int length = str.length();
            for (int i = 0; i < length; i++) {
                counts[str.charAt(i) - 'a']++;//计数,统计字母出现次数
            }
            // 将每个出现次数大于 0 的字母和出现次数按顺序拼接成字符串,作为哈希表的键
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < 26; i++) {
                if (counts[i] != 0) {
                    sb.append((char) ('a' + i));
                    sb.append(counts[i]);
                }
            }
            String key = sb.toString();
            List<String> list = map.getOrDefault(key, new ArrayList<String>());
            list.add(str);
            map.put(key, list);
        }
        return new ArrayList<List<String>>(map.values());
    }
}

6.两数之和(1)

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

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

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

示例 1:

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

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

提示:

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

**进阶:**你可以想出一个时间复杂度小于 O(n2) 的算法吗?

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> ha;
        for(int i = 0; i < nums.size(); i++){
            auto it = ha.find(target - nums[i]);//从后往前找
            if(it != ha.end()){
                return {it -> second, i};//返回值和此处的索引
            }
            ha[nums[i]] = i;
        }
        return {};
    }
};
class Solution {//java版
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> ha = new HashMap<Integer, Integer>();
        int[] res = new int[2];
        for(int i = 0; i < nums.length; i++){
            if(ha.containsKey(target - nums[i])){
                res[0] = ha.get(target -nums[i]);
                res[1] = i;
                return res;
            }
            ha.put(nums[i], i);
        }
        return res;
    }
}

7.快乐数(202)

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n快乐数 就返回 true ;不是,则返回 false

示例 1:

输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

示例 2:

输入:n = 2
输出:false

提示:

  • 1 <= n <= 231 - 1
法一:利用集合,判断循环是否是1
class Solution {
private:
    int getSumSquare(int n){
        int ans = 0;
        while(n){
            int d = n % 10;
            ans += d * d;
            n /= 10;
        }
        return ans;
    }
public:
    bool isHappy(int n) {
        set<int> s;
        while(n != 1 && !s.contains(n)){
            s.insert(n);
            n = getSumSquare(n);
        }
        return n == 1;
    }
};
class Solution {//java版
    private int getSumSquare(int n){
        int ans = 0;
        while(n > 0){
            int d = n % 10;
            ans += d * d;
            n /= 10;
        }
        return ans;
    }
    public boolean isHappy(int n) {
        Set<Integer> s = new HashSet<Integer>();
        while(n != 1 && !s.contains(n)){//当求和后在集合里面找到时,说明出现了循环,退出,判断是否是1
            s.add(n);//加入到集合中
            n = getSumSquare(n);
        }
        return n == 1;
    }
}
法二:快慢指针
class Solution {
     public int getNext(int n) {
        int totalSum = 0;
        while (n > 0) {
            int d = n % 10;
            n = n / 10;
            totalSum += d * d;
        }
        return totalSum;
    }

    public boolean isHappy(int n) {
        int slowRunner = n;
        int fastRunner = getNext(n);
        while (fastRunner != 1 && slowRunner != fastRunner) {
            slowRunner = getNext(slowRunner);
            fastRunner = getNext(getNext(fastRunner));
        }
        return fastRunner == 1;
    }
}

8.存在重复元素II(219)

给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 ij ,满足 nums[i] == nums[j]abs(i - j) <= k 。如果存在,返回 true ;否则,返回 false

示例 1:

输入:nums = [1,2,3,1], k = 3
输出:true

示例 2:

输入:nums = [1,0,1,1], k = 1
输出:true

示例 3:

输入:nums = [1,2,3,1,2,3], k = 2
输出:false

提示:

  • 1 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • 0 <= k <= 105
法一:哈希表,常规方法,简单
class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        unordered_map<int, int> ha;
        for(int i = 0; i < nums.size(); i++){
            if(ha.count(nums[i])){
                if(i - ha[nums[i]] <= k){
                    return true;
                }
            }
            ha[nums[i]] = i;
        }
        return false;
    }
};
//Java版
class Solution {
    public boolean containsNearbyDuplicate(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        int length = nums.length;
        for (int i = 0; i < length; i++) {
            int num = nums[i];
            if (map.containsKey(num) && i - map.get(num) <= k) {
                return true;
            }
            map.put(num, i);
        }
        return false;
    }
}
法二:滑动窗口
class Solution {
    public boolean containsNearbyDuplicate(int[] nums, int k) {
        Set<Integer> set = new HashSet<Integer>();
        int length = nums.length;
        for (int i = 0; i < length; i++) {
            if (i > k) {
                set.remove(nums[i - k - 1]);//c++版为erase
            }
            if (!set.add(nums[i])) {//c++版为count
                return true;
            }
        }
        return false;
    }
}
考虑数组 nums 中的每个长度不超过 k+1的滑动窗口,同一个滑动窗口中的任意两个下标差的绝对值不超过 k。如果存在一个滑动窗口,其中有重复元素,则存在两个不同的下标 i 和 j满足 nums[i]=nums[j]且 ∣i−j∣≤k。如果所有滑动窗口中都没有重复元素,则不存在符合要求的下标。因此,只要遍历每个滑动窗口,判断滑动窗口中是否有重复元素即可。
如果一个滑动窗口的结束下标是 i,则该滑动窗口的开始下标是 max⁡(0,i−k)。可以使用哈希集合存储滑动窗口中的元素。从左到右遍历数组 nums,当遍历到下标 i 时,具体操作如下:

如果 i>k,则下标 i−k−1处的元素被移出滑动窗口,因此将 nums[i−k−1] 从哈希集合中删除;

判断 nums[i]是否在哈希集合中,如果在哈希集合中则在同一个滑动窗口中有重复元素,返回 true,如果不在哈希集合中则将其加入哈希集合。

当遍历结束时,如果所有滑动窗口中都没有重复元素,返回 false。

9.最长连续序列(128)

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

示例 2:

输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        if(nums.size() <= 0)
            return nums.size();
        sort(nums.begin() , nums.end());
        int ans = 0;
        int pre = nums[0];
        int s = 1;
        for(int i = 1 ; i < nums.size() ; i++){
            if(nums[i] == pre){
                continue;
            }
            if(nums[i] != pre + 1){
                ans = max(ans , s);
                s = 0;
            }
            pre = nums[i];
            s++;
        }
        return max(ans , s);           
    }
};
class Solution {//java版
    public int longestConsecutive(int[] nums) {
       if(nums.length == 0) {
           return 0;
       }
       // 用于判定元素是否重复
       Set<Integer> set = new HashSet<>();
       int length = 1;
       int maxLength = 1;
       // 排序
       Arrays.sort(nums);
       for(int i =1; i< nums.length; i++) {
           // 当元素存在时, 保持length不变。 
           if( ! set.add(nums[i])) {
               continue;
           }
           // 连续的元素: 翻译下:就是nums[i-1] + 1 = nums[i]
           if(nums[i-1] + 1 == nums[i]) {
               length ++;
               // 当出现多串连续元素时, 取最大length: 。
               maxLength = Math.max(length, maxLength);
           }else{
               length = 1;
           }
           
       }
       return maxLength;
   }
}

10.汇总区间(228)

给定一个 无重复元素有序 整数数组 nums

返回 恰好覆盖数组中所有数字最小有序 区间范围列表 。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于 nums 的数字 x

列表中的每个区间范围 [a,b] 应该按如下格式输出:

  • "a->b" ,如果 a != b
  • "a" ,如果 a == b

示例 1:

输入:nums = [0,1,2,4,5,7]
输出:["0->2","4->5","7"]
解释:区间范围是:
[0,2] --> "0->2"
[4,5] --> "4->5"
[7,7] --> "7"

示例 2:

输入:nums = [0,2,3,4,6,8,9]
输出:["0","2->4","6","8->9"]
解释:区间范围是:
[0,0] --> "0"
[2,4] --> "2->4"
[6,6] --> "6"
[8,9] --> "8->9"

提示:

  • 0 <= nums.length <= 20
  • -231 <= nums[i] <= 231 - 1
  • nums 中的所有值都 互不相同
  • nums 按升序排列
class Solution {
public:
    vector<string> summaryRanges(vector<int>& nums) {
        vector<string> ret;
        int i = 0;
        int n = nums.size();
        while (i < n) {
            int low = i;
            i++;
            while (i < n && nums[i] == nums[i - 1] + 1) {
                i++;
            }
            int high = i - 1;
            string temp = to_string(nums[low]);
            if (low < high) {
                temp.append("->");
                temp.append(to_string(nums[high]));
            }
            ret.push_back(move(temp));
        }
        return ret;
    }
};
class Solution {//java
    public List<String> summaryRanges(int[] nums) {
        List<String> ret = new ArrayList<String>();
        int i = 0;
        int n = nums.length;
        while (i < n) {
            int low = i;//从高处-1判断更合适,low能考虑0的情况
            i++;//从1开始往前比
            while (i < n && nums[i] == nums[i - 1] + 1) {//连续数字
                i++;
            }
            int high = i - 1;//i+1后不满足条件则-1
            StringBuffer temp = new StringBuffer(Integer.toString(nums[low]));//可变字符串先插入low
            if (low < high) {
                temp.append("->");
                temp.append(Integer.toString(nums[high]));
            }
            ret.add(temp.toString());
        }
        return ret;
    }
}

11.合并区间(56)

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

示例 2:

输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

提示:

  • 1 <= intervals.length <= 104
  • intervals[i].length == 2
  • 0 <= starti <= endi <= 104
class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        if (intervals.size() == 0) {
            return {};
        }
        sort(intervals.begin(), intervals.end());
        vector<vector<int>> merged;
        for (int i = 0; i < intervals.size(); ++i) {
            int L = intervals[i][0], R = intervals[i][1];
            if (!merged.size() || merged.back()[1] < L) {
                merged.push_back({L, R});
            }
            else {
                merged.back()[1] = max(merged.back()[1], R);
            }
        }
        return merged;
    }
};
class Solution {//java版
    public int[][] merge(int[][] intervals) {
        if (intervals.length == 0) {
            return new int[0][2];//长度为0则输出新数组(为0)
        }
        Arrays.sort(intervals, new Comparator<int[]>() {//按照新的排序规则排序
            public int compare(int[] interval1, int[] interval2) {
                return interval1[0] - interval2[0];//左端点升序排序
            }
        });
        List<int[]> merged = new ArrayList<int[]>();
        for (int i = 0; i < intervals.length; ++i) {
            int L = intervals[i][0], R = intervals[i][1];
            if (merged.size() == 0 || merged.get(merged.size() - 1)[1] < L) {
                merged.add(new int[]{L, R});//第一个直接加入merged,如果后面左端点小于merged右也插入
            } else {//更新右端点
                merged.get(merged.size() - 1)[1] = Math.max(merged.get(merged.size() - 1)[1], R);
            }
        }
        return merged.toArray(new int[merged.size()][]);
    }
}

12.插入区间(57)

给你一个 无重叠的 *,*按照区间起始端点排序的区间列表。

在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。

示例 1:

输入:intervals = [[1,3],[6,9]], newInterval = [2,5]
输出:[[1,5],[6,9]]

示例 2:

输入:intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
输出:[[1,2],[3,10],[12,16]]
解释:这是因为新的区间 [4,8] 与 [3,5],[6,7],[8,10] 重叠。

示例 3:

输入:intervals = [], newInterval = [5,7]
输出:[[5,7]]

示例 4:

输入:intervals = [[1,5]], newInterval = [2,3]
输出:[[1,5]]

示例 5:

输入:intervals = [[1,5]], newInterval = [2,7]
输出:[[1,7]]

提示:

  • 0 <= intervals.length <= 104
  • intervals[i].length == 2
  • 0 <= intervals[i][0] <= intervals[i][1] <= 105
  • intervals 根据 intervals[i][0]升序 排列
  • newInterval.length == 2
  • 0 <= newInterval[0] <= newInterval[1] <= 105
class Solution {
public:
    vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
        int left = newInterval[0];
        int right = newInterval[1];
        bool placed = false;
        vector<vector<int>> ans;
        for (const auto& interval: intervals) {
            if (interval[0] > right) {
                // 在插入区间的右侧且无交集
                if (!placed) {
                    ans.push_back({left, right});
                    placed = true;                    
                }
                ans.push_back(interval);
            }
            else if (interval[1] < left) {
                // 在插入区间的左侧且无交集
                ans.push_back(interval);
            }
            else {
                // 与插入区间有交集,计算它们的并集
                left = min(left, interval[0]);
                right = max(right, interval[1]);
            }
        }
        if (!placed) {
            ans.push_back({left, right});
        }
        return ans;
    }
};
class Solution {//java
    public int[][] insert(int[][] intervals, int[] newInterval) {
        List<int[]> l = new ArrayList<int[]>();
        if(intervals.length == 0){
            l.add(newInterval);
            return l.toArray(new int[l.size()][]);
        }
        boolean flag = false;
        int left = newInterval[0], right = newInterval[1];
        for(int i = 0; i < intervals.length; i++){
            if(intervals[i][0] > right){//小区间的左边大于新区间右边(即不影响)
                if(!flag){//如果没有插入新区间
                    l.add(new int[]{left, right});//newInternal已经更新了;
                    flag = true;
                }
                l.add(intervals[i]);//不妨碍插入此小区间
            }
            else if(intervals[i][1] < left){//小区间右边小于新区间左边(即不影响)
                l.add(intervals[i]);//右边插入newInternal的情况默认到最后flag判断
            }
            else{//有交集,修改newInternal的左右的值
                left = Math.min(left, intervals[i][0]);
                right = Math.max(right, intervals[i][1]);//不急着插入,在左侧时插入
            }
        }
        if(flag == false){
            l.add(new int[]{left, right});//newInternal已经更新了
        }
        return l.toArray(new int[l.size()][]);
    }
}

13.用最少数量的箭引爆气球(452)

有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstartxend之间的气球。你不知道气球的确切 y 坐标。

一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 x``startx``end, 且满足 xstart ≤ x ≤ x``end,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。

给你一个数组 points返回引爆所有气球所必须射出的 最小 弓箭数

示例 1:

输入:points = [[10,16],[2,8],[1,6],[7,12]]
输出:2
解释:气球可以用2支箭来爆破:
-在x = 6处射出箭,击破气球[2,8]和[1,6]。
-在x = 11处发射箭,击破气球[10,16]和[7,12]。

示例 2:

输入:points = [[1,2],[3,4],[5,6],[7,8]]
输出:4
解释:每个气球需要射出一支箭,总共需要4支箭。

示例 3:

输入:points = [[1,2],[2,3],[3,4],[4,5]]
输出:2
解释:气球可以用2支箭来爆破:
- 在x = 2处发射箭,击破气球[1,2]和[2,3]。
- 在x = 4处射出箭,击破气球[3,4]和[4,5]。

提示:

  • 1 <= points.length <= 105
  • points[i].length == 2
  • -231 <= xstart < xend <= 231 - 1
class Solution {
public:
    int findMinArrowShots(vector<vector<int>>& points) {
        if (points.empty()) {
            return 0;
        }
        sort(points.begin(), points.end(), [](const vector<int>& u, const vector<int>& v) {
            return u[1] < v[1];
        });
        int pos = points[0][1];
        int ans = 1;
        for (const vector<int>& balloon: points) {
            if (balloon[0] > pos) {
                pos = balloon[1];
                ++ans;
            }
        }
        return ans;
    }
};
法一:左端点排序
class Solution {
    public int findMinArrowShots(int[][] points) {
        Arrays.sort(points, new Comparator<int[]> (){
            public int compare(int[] points1, int[] points2){//左端点排序
               if (points1[0] > points2[0]){
                        return 1;
                }else if (points1[0] < points2[0]){
                    return -1;
                }else{
                    return 0;
                }
            }
        });
        int cnt = 1;
        int rightMin = points[0][1];//默认右边最小值
        for(int i = 1; i < points.length; i++){
            if(points[i][0] > rightMin){//左边的值大于默认右边最小值,需要跳过
                rightMin = points[i][1];//更新右边默认最小值
                cnt++;
            }
            else{
                rightMin = Math.min(rightMin, points[i][1]);//非跳过的情况
            }
        }
        return cnt;
    }
}
法二:右端点排序
public int findMinArrowShots(int[][] points) {
     Arrays.sort(points, Comparator.comparingLong((int[] pre) -> pre[1]));
     int res = 1;
     int rightMin = points[0][1];

     for (int i = 0; i < points.length; i++) {
         if (points[i][0] > rightMin) {
             rightMin = points[i][1];
             res++;
         }
     }
        return res;
 }
  • 19
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值