leetcode hot100刷题(1)

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> hashtable;
        for (int i = 0; i < nums.size(); ++i) {
            auto it = hashtable.find(target - nums[i]);
            if (it != hashtable.end()) {
                return {it->second, i};
            }
            hashtable[nums[i]] = i;
        }
        return {};
    }
};

注意:如果不采用这种一般将数据压入hashtable一边判断而是采用先将所有数据全部压入hashtable后再一个给判断过不了样例2

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] 仅包含小写字母

方法一:排序

时间复杂度:$O(nklogk)$,其中 n 是 strs 中的字符串的数量,k是 strs 中的字符串的的最大长度。

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string,vector<string>> hashtable;
        vector<string> tmp(strs);
        for(int i=0;i<strs.size();i++) sort(tmp[i].begin(),tmp[i].end());
        for(int i=0;i<strs.size();i++)
            hashtable[tmp[i]].push_back(strs[i]);
        vector<vector<string>> res;
        for(auto it:hashtable) res.push_back(it.second);
        return res;
    }
};

注意:下面代码只会对tmp的副本排序,所以tmp[i]仍然没有被排序,需要将for(string s:tmp) sort(s.begin(),s.end());改成for(string& s:tmp) sort(s.begin(),s.end());

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string,vector<string>> hashtable;
        vector<string> tmp(strs);
        for(string s:tmp) sort(s.begin(),s.end());
        for(int i=0;i<strs.size();i++)
            hashtable[tmp[i]].push_back(strs[i]);
        vector<vector<string>> res;
        for(auto it:hashtable) res.push_back(it.second);
        return res;
    }
};

他山之石

由于互为字母异位词的两个字符串包含的字母相同,因此两个字符串中的相同字母出现的次数一定是相同的,故可以将每个字母出现的次数使用字符串表示,作为哈希表的键。

由于字符串只包含小写字母,因此对于每个字符串,可以使用长度为 26 的数组记录每个字母出现的次数。需要注意的是,在使用数组作为哈希表的键时,不同语言的支持程度不同,因此不同语言的实现方式也不同。

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        // 自定义对 array<int, 26> 类型的哈希函数
        auto arrayHash = [fn = hash<int>{}] (const array<int, 26>& arr) -> size_t {
            return accumulate(arr.begin(), arr.end(), 0u, [&](size_t acc, int num) {
                return (acc << 1) ^ fn(num);
            });
        };
        unordered_map<array<int, 26>, vector<string>, decltype(arrayHash)> mp(0, arrayHash);
        for (string& str: strs) {
            array<int, 26> counts{};
            int length = str.length();
            for (int i = 0; i < length; ++i) {
                counts[str[i] - 'a'] ++;
            }
            mp[counts].emplace_back(str);
        }
        vector<vector<string>> ans;
        for (auto it = mp.begin(); it != mp.end(); ++it) {
            ans.emplace_back(it->second);
        }
        return ans;
    }
};

原文链接:https://leetcode.cn/problems/group-anagrams/solutions/520469/zi-mu-yi-wei-ci-fen-zu-by-leetcode-solut-gyoc/

语法学习

std::array 是 C++ 标准库提供的静态数组,长度固定,但有更多功能,如边界检查

C++11 之后,vector 容器中添加了新的方法:emplace_back() ,和 push_back() 一样的是都是在容器末尾添加一个新的元素进去,不同的是 emplace_back() 在效率上相比较于 push_back() 有了一定的提升。

#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<pair<int, string>> vec;
    // 使用 push_back 需要显式构造对象
    vec.push_back(make_pair(1, "Apple"));
    // 使用 emplace_back 直接构造对象
    vec.emplace_back(2, "Banana");
    for (const auto& p : vec) {
        cout << p.first << " - " << p.second << endl;
    }
    return 0;
}

C++中push_back和emplace_back的区别 - 知乎

自定义hash函数

在C++中如何对自定义类型做hash操作_c++自定义哈希函数-CSDN博客

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

示例 3:

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

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        set<int> s(nums.begin(),nums.end());
        nums.clear();
        for(int num:s) nums.push_back(num);
        int res=(nums.size()!=0);
        int l=0,r=1;
        int n=nums.size();
        while(r<n){
            while(r<n&&nums[r]-nums[r-1]==1){
                r++;
            }
            res=max(res,r-l);
            l=r;r=r+1;
        }
        return res;
    }
};

他山之石

本题不能排序,因为排序的时间复杂度是 $O(n\log n)$,不符合题目 $O(n)$ 的要求。

核心思路:

对于 nums 中的元素 x,以 x 为起点,不断查找下一个数 x+1, x+2, ... 是否在 nums 中,并统计序列的长度。

关键优化(使时间复杂度达到 $O(n)$):

  1. 使用哈希集合

    • nums 中的数存入哈希集合,这样可以 $O(1)$ 判断数字是否存在。
  2. 避免重复计算

    • 如果 x-1 存在于哈希集合中,则不以 x 为起点
    • 这样可以保证我们只会从连续序列的第一个数开始计算,避免大量重复计算。
    • 例如 nums = [3,2,4,5]
      • 3 开始,我们可以找到 3,4,5 这个连续序列。
      • 2 开始,我们可以找到 2,3,4,5,一定比从 3 开始的更长,因此 3 不是一个合适的起点。

细节优化:

  • 遍历哈希集合,而不是 nums 本身!
    • 例如 nums = [1,1,1,...,1,2,3,4,5,...](前一半都是 1)。
    • 遍历 nums 会导致每个 1 都执行 $O(n)$ 的循环,总复杂度变为 $O(n^2)$,从而超时。
class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        int res=0;
        unordered_set<int> hashtable(nums.begin(),nums.end());
        for(int x:hashtable){
            if(hashtable.contains(x-1)) continue;
            //x是序列的起点
            int y=x+1;
            while(hashtable.contains(y)) y++;
            //循环结束后,y-1是最后一个在连续数
            res=max(res,y-x);
        }
        return res;
    }
};

语法学习

从C++11到C++23(六)C++20利用contains查询map是否存在某个键_c++ map contains-CSDN博客

C++20新增了std::map::contains可以直接查找键是否存在,返回值类型为bool型

#include <iostream>
#include <string>
#include <map>

int main()
{
    std::map<int, std::string> example = {{1, "One"}, {2, "Two"}, 
                                     {3, "Three"}, {42, "Don\'t Panic!!!"}};

    if(example.contains(42)) {
        std::cout << "Found\n";
    } else {
        std::cout << "Not found\n";
    }
}

283. 移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例 1:

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

示例 2:

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

提示:

  • 1 <= nums.length <= 104
  • -231 <= nums[i] <= 231 - 1

进阶:你能尽量减少完成的操作次数吗?

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int i=0,j=0;
        int n=nums.size();
        while(i<n&&j<n){
            while(n>i&&nums[i]) i++;
            j=i+1;
            while(n>j&&!nums[j]) j++;
            if(n>i,n>j) swap(nums[i],nums[j]);
            i++;
        }
    }
};

他山之石

📌 核心思路

我们将 0 视作 空位,把所有 非零元素 依次移动到数组左侧的空位,同时 保持原有顺序

示例

cpp复制编辑输入: nums = [0, 1, 0, 3, 12]
输出: [1, 3, 12, 0, 0]

💡 关键点

  1. 维护一个指针 i0,指向最左侧的 空位(即 0)。
  2. 遍历数组
    • 遇到非零元素 → 交换 nums[i]nums[i0],然后 i0++
    • 遇到 0 → 不操作,继续遍历。

📜 代码

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int i0 = 0;  // 记录最左侧空位
        for (int& x : nums) { // 
            if (x) { // 只有非零元素才交换
                swap(x, nums[i0]);
                i0++; // 更新最左侧空位
            }
        }
    }
};

📊 复杂度分析

  • 时间复杂度:O(n),遍历 nums 一次,每个元素最多交换一次。
  • 空间复杂度:O(1),原地修改,没有额外空间消耗。

🧐 运行过程解析

示例

输入: nums = [0, 1, 0, 3, 12]
i i0 nums[i] 交换后 nums
0 0 0 不操作
1 0 1 [1, 0, 0, 3, 12]
2 1 0 不操作
3 1 3 [1, 3, 0, 0, 12]
4 2 12 [1, 3, 12, 0, 0]

最终输出:

[1, 3, 12, 0, 0]

💬 可能的疑问

❓ Q1: 为什么可以i0=0

  • 实际上i0会自动找到第一个为0的元素
  • 例如

    nums = [1, 2, 3, 4,0,12]
    
    1. 发现 1,交换 nums[0]nums[0][1, 2, 3, 4,0,12],i0=1
    2. 发现 2,交换 nums[1]nums[1][1, 2, 3, 4,0,12],i0=2
    3. 发现 3,交换 nums[2]nums[2][1, 2, 3, 4,0,12],i0=3
    4. 发现 4,交换 nums[3]nums[3][1, 2, 3, 4,0,12],i0=4
    5. 发现 0,交换 nums[4]nums[4][1, 2, 3, 4,0,12],i0=4
    6. 发现 12,交换 nums[5]nums[4][1, 2, 3, 4,12,0],i0=5

原文链接

posted @ 2025-03-06 23:35  dlNie  阅读(121)  评论(0)    收藏  举报