算法过程中不会的

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

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

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

这也是力扣hot100第一题简单。以下是暴力解法。

cpp 复制代码
return tmp;
return {};

return{},返回一个 "空的、默认构造的" 对象,等价于返回该类型的默认值

cpp 复制代码
std::string getStr() {
    std::string tmp = "hello";
    
    // return tmp;   // 返回 "hello"
    return {};        // 返回空字符串 ""
}
cpp 复制代码
int getNum() {
    return {};  // 返回 0
}
cpp 复制代码
int* getPtr() {
    return {};  // 返回 nullptr 空指针
}

这是 C++11 之后的列表初始化返回,超级常用。

cpp 复制代码
// tmp.emplace_back(i);  // 往tmp里加i
// tmp.emplace_back(j);  // 往tmp里加j
// return tmp;           // 返回装满数据的tmp

return {i, j};           // 直接返回 {i,j},效果和上面完全一样!
cpp 复制代码
// 原来的写法(啰嗦)
vector<int> get() {
    vector<int> tmp;
    tmp.emplace_back(10);
    tmp.emplace_back(20);
    return tmp;
}

// 现在的写法(简洁高效)
vector<int> get() {
    return {10, 20};
}

哈希表。

注意到方法一的时间复杂度较高的原因是寻找 target - x 的时间复杂度过高。

使用哈希表,可以将寻找 target - x 的时间复杂度降低到从 O(N) 降低到 O(1)。

cpp 复制代码
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 {};
    }
};

这种找对应的数,可以想到哈希表降复杂度。第一次找target-nums[i]肯定是找不到的,因为vector里面的数没有导入哈希表,第一次没找到把数据插入,第二次就可以找到对应的。

这是找到一个相加等于target,找到所有的又不一样。。。


两个字符串互为字母异位词,当且仅当两个字符串包含的字母相同。同一组字母异位词中的字符串具备相同点,可以使用相同点作为一组字母异位词的标志,使用哈希表存储每一组字母异位词,哈希表的键为一组字母异位词的标志,哈希表的值为一组字母异位词列表。

遍历每个字符串,对于每个字符串,得到该字符串所在的一组字母异位词的标志,将当前字符串加入该组字母异位词的列表中。遍历全部字符串之后,哈希表中的每个键值对即为一组字母异位词。

以下的两种方法分别使用排序和计数作为哈希表的键。

  • 由于互为字母异位词的两个字符串包含的字母相同,因此对两个字符串分别进行排序之后得到的字符串一定是相同的,故可以将排序之后的字符串作为哈希表的键。

    cpp 复制代码
    class Solution {
    public:
        vector<vector<string>> groupAnagrams(vector<string>& strs) {
            unordered_map<string, vector<string>> mp;
            for (string& str: strs) {
                string key = str;
                sort(key.begin(), key.end());
                mp[key].emplace_back(str);
            }
            vector<vector<string>> ans;
            for (auto it = mp.begin(); it != mp.end(); ++it) {
                ans.emplace_back(it->second);
            }
            return ans;
        }
    };

    (竟如此简单变得,,sort排序后的值对应不同异位词,,)

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

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

    cpp 复制代码
    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;
        }
    };

    复杂,,,

前面这段是lambda函数,但 lambda 没有名字,没法直接写类型,所以decltype(arrayHash),获取一个变量 / 表达式的 "类型"

C++ 的 unordered_map 不支持直接把 array 当 key!必须自己给它写一个哈希方法,才能用 array 做 key!

...............................................

以下是我的错误。简直致命。。

for(int i=0;i<hash.size();i++) → 遍历方式错误,哈希表不能用下标 i 遍历

tmp.push_back(*hash[i]->second); → 语法完全错误

for(auto &it:hash){
tmp.push_back(it.second);
}

cpp 复制代码
class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string,vector<string>> hash;
        vector<vector<string>> tmp;
        for(auto str:strs){
            string key=str;
            sort(key.begin(),key.end());
            hash[key].emplace_back(str);
        }
        for(auto &it:hash){
            tmp.push_back(it.second);
        }
        return tmp;
    }

};

除了哈希表,还能双层暴力,或用map与哈希表类似,逻辑一模一样,只是底层不是哈希。是红黑树。。

还有

计数编码成字符串当 key(不用排序、自定义哈希)

把每个单词的 26 字母计数,拼成一个特制字符串当 key,例如:a:2 b:1 → "2,1,0,..."用这个字符串做 key,存入普通 map,既不用排序、也不用手写 lambda 哈希。

cpp 复制代码
string getKey(string &s) {
    int cnt[26]{};
    for(char c : s) cnt[c-'a']++;
    string key;
    for(int i=0;i<26;i++) {
        key += to_string(cnt[i]) + ",";
    }
    return key;
}

vector<vector<string>> groupAnagrams(vector<string>& strs) {
    map<string, vector<string>> mp;
    for(auto &s : strs) {
        string key = getKey(s);
        mp[key].push_back(s);
    }
    vector<vector<string>> ans;
    for(auto &p : mp) ans.push_back(p.second);
    return ans;
}

感觉不如哈希了,,


int num=1; int sum=1;与int num, sum=1;

错了好几次了。。第二个

  • num = 1
  • sum = 1
  • sum = 1
  • num = 未初始化(乱码)
cpp 复制代码
class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        if (nums.empty()) return 0;
        sort(nums.begin(),nums.end());
        
        int num=1;
        int sum=1;
        for(int i=1;i<nums.size();i++){
            if(nums[i]==nums[i-1]+1){
                num++;
            }
            else if (nums[i] != nums[i-1]) {
                num=1;
            }
            sum=max(sum,num);
        }
        return sum;
    }
};

然后题解里面用set去重,简单来说就是每个数都判断一次这个数是不是连续序列的开头那个数

然后count找下一位,记录起来,,

unordered_set里面,count查集合里有没有这个数!有返回 1,没有返回 0!

cpp 复制代码
class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        unordered_set<int> num_set;
        for (const int& num : nums) {
            num_set.insert(num);
        }

        int longestStreak = 0;

        for (const int& num : num_set) {
            if (!num_set.count(num - 1)) {
                int currentNum = num;
                int currentStreak = 1;

                while (num_set.count(currentNum + 1)) {
                    currentNum += 1;
                    currentStreak += 1;
                }

                longestStreak = max(longestStreak, currentStreak);
            }
        }

        return longestStreak;           
    }
};

以下这个简便一点

cpp 复制代码
class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        int res = 0;    // 记录最长连续序列的长度
        unordered_set<int> num_set(nums.begin(), nums.end());   // 记录nums中的所有数值
        int seqLen;
        for(int num: num_set){
            // 如果当前的数是一个连续序列的起点,统计这个连续序列的长度
            if(!num_set.count(num - 1)){
                seqLen = 1;     // 连续序列的长度,初始为1
                while(num_set.count(++num))seqLen++;    // 不断查找连续序列,直到num的下一个数不存在于数组中
                res = max(res, seqLen);     // 更新最长连续序列长度
            }
        }
        return res;
    }
};

count换成contains也可以。。

  • count :C++ 老版本用,返回 1/0
  • contains :C++20 新功能,返回 true/false

一、unordered_set 常用接口(去重 + 查找)

复制代码
unordered_set<int> s;
1. 插入元素
复制代码
s.insert(10);
2. 查找元素(最重要)
复制代码
s.count(10);   // 有返回1,无返回0  🔥刷题最常用
s.contains(10);// C++20,有true,无false
3. 遍历
复制代码
for (auto x : s) { }
4. 大小 / 清空
复制代码
s.size();
s.clear();

二、unordered_map 常用接口(key-value 映射)

复制代码
unordered_map<int, string> hash;
1. 插入 / 修改
复制代码
hash[1] = "a";
hash.insert({2, "b"});
2. 查找 key
复制代码
hash.count(1);     // 有1,无0  🔥最常用
hash.find(1) != hash.end(); // 查找成功
3. 取值
复制代码
hash[1];   // 获取 key=1 对应的 value
4. 遍历
复制代码
for (auto& pair : hash) {
    pair.first;   // key
    pair.second;  // value
}

set去重把数据放回原数组

cpp 复制代码
vector<int> nums = {1,1,2,2,3};
unordered_set<int> s(nums.begin(), nums.end()); // 去重

// 把 set 里的数据,全部赋值给 nums
nums.assign(s.begin(), s.end()); 

assign = 把一堆数据,直接覆盖赋值给 vector

就是把别的容器里的东西,全部倒进 vector 里,原来的数据全部丢掉

cpp 复制代码
nums = {1,2,3} (原来的重复数据没了)
① 把另一个 vector 赋值过来
复制代码
v1.assign(v2.begin(), v2.end());
② 把 set 赋值过来(你刚才用的)
复制代码
nums.assign(s.begin(), s.end());
③ 赋 5 个 10
复制代码
v.assign(5, 10);
// v = {10,10,10,10,10}

范围 for 循环。 语法糖(Syntactic sugar)。C++11新特性

1)只读(拷贝,不能改原数组)
复制代码
for(auto e : nums)
2)引用(能修改原数组,最常用)✅
cpp 复制代码
for(auto &e : nums)
{
    e = 100;  // 可以修改 nums 里的元素
}
3)常量引用(只读,不拷贝、不能改)
复制代码
for(const auto &e : nums)
你原来代码修正,unordered_set<int> st(nums.begin(), nums.end());
cpp 复制代码
unordered_set<int> st;
for(auto &e : nums){
    st.insert(e);
}
用 set 一键去重更简单,上下等价
cpp 复制代码
unordered_set<int> st(nums.begin(), nums.end());
// 再导回 vector
nums.assign(st.begin(), st.end());

编译器是从左往右逐个定义,C++ 变量赋值 = 只赋值一次,不是绑定关系!

int l=0,r=m+1,m=l+1;完全错误,按顺序定义的m还没定义就定义r。。。不可以


cpp 复制代码
  for(int l=0;l<nums.size();l++){
            if(nums[l]==nums[l+1]){
                continue;

不管是l,l+1还是nums[l]==nums[l-1]都会越界,应该多加一个l>0条件

关键在于

cpp 复制代码
 int n = nums.size();
        // 第一个数 l
        for (int l = 0; l < n; l++) {
            // 去重!关键:和前一个相同就跳过
            if (l > 0 && nums[l] == nums[l-1]) continue;

cpp 复制代码
vector<int> ans;
if (!root) return ans;
cpp 复制代码
if(root==nullptr) return {};

这两个相同


在二叉树层序遍历时候,queue队列没有 .length() 队列用的是 .size()

给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        if(root==nullptr) return {};
        vector<int> ans;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
            int len=q.size();
            for(int i=0;i<len;i++){
                TreeNode* cur=q.front();
                q.pop();
                if(i==len-1){
                    ans.push_back(cur->val);
                }
                if(cur->left) q.push(cur->left);
                if(cur->right) q.push(cur->right);
            }
        }
        
        return ans;
    }
};

q.pop(*cur) 不行?队列的 pop() 没有参数! 它就是直接删除队首元素,不能填东西。

相关推荐
阿旭超级学得完1 小时前
C++11包装器(function和bind)
java·开发语言·c++·算法·哈希算法·散列表
輕華1 小时前
uv工具详解——Python包与项目管理器完全指南
开发语言·python·uv
li星野1 小时前
位运算 & 数学 & 高频进阶九题通关(Python + C++)
c++·python·学习·算法
念何架构之路2 小时前
Go语言常见并发模式
开发语言·后端·golang
磊 子2 小时前
多态类原理+四种类型转换+异常处理
开发语言·c++·算法
脆皮炸鸡7552 小时前
库制作与原理~动态链接
linux·开发语言·经验分享·笔记·学习方法
XMYX-02 小时前
26 - Go recover 捕获错误:优雅恢复的真正意义
开发语言·golang
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之字符串 --【回文字符串】:回文拼接
c++·字符串·csp·高频考点·信奥赛·字符串回文·回文拼接