目录

主要思路:
基于从下到上的 dp 回溯法,每一个节点只有两种状态,dp[0]表示被打劫,dp[1]表示不被打劫;
当前节点被打劫时,其孩子只能都不被打劫;dp[0] = left[1] + right[1] + cur->val;
当前节点不被打劫时,其孩子可以都被打劫,也可以都不被打劫,或者一个被打劫另一个不被打劫。
dp[1] = max(left[0] + right[0], left[1] + right[1], left[0] + right[1], left[1] + right[0]);
- #include
- #include
-
- 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:
- int rob(TreeNode* root) {
- // 对于每一间房屋,有被打劫和不被打劫两种状态
- std::vector<int> res = dfs(root);
- return std::max(res[0], res[1]);
- }
-
- std::vector<int> dfs(TreeNode* cur){
- if(cur == nullptr) return {0, 0};
- std::vector<int> left = dfs(cur->left);
- std::vector<int> right = dfs(cur->right);
- std::vector<int> dp(2, 0); // dp[0]表示被打劫 dp[1]表示不被打劫
- dp[0] = left[1] + right[1] + cur->val; // 当前房屋被打劫,其孩子只能不被打劫
- // 当前房屋不被打劫,其孩子可以同时被打劫,也可以同时不被打劫
- // 当前房屋不被打劫,其孩子可以一个被打劫,另一个不被打劫
- dp[1] = std::max(std::max(std::max(left[0] + right[0], left[1] + right[1]), left[0] + right[1]), left[1] + right[0]);
- return dp;
- }
- };
-
- int main(int argc, char* argv[]){
- // root = [3, 2, 3, null, 3, null, 1]
- TreeNode *Node1 = new TreeNode(3);
- TreeNode *Node2 = new TreeNode(2);
- TreeNode *Node3 = new TreeNode(3);
- TreeNode *Node4 = new TreeNode(3);
- TreeNode *Node5 = new TreeNode(1);
-
- Node1->left = Node2;
- Node1->right = Node3;
- Node2->right = Node4;
- Node3->right = Node5;
-
- Solution S1;
- int res = S1.rob(Node1);
- std::cout << res << std::endl;
- return 0;
- }

主要思路:
- #include
- #include
-
- class Solution {
- public:
- std::vector<int> countBits(int n) {
- std::vector<int> dp(n + 1, 0);
- int high_valid = 0;
- for(int i = 1; i <= n; i++){
- if((i & (i - 1)) == 0){ // i是2的指数幂,更新最高有效位
- high_valid = i;
- }
- dp[i] = dp[i - high_valid] + 1;
- }
- return dp;
- }
- };
-
- int main(int argc, char* argv[]){
- // n = 5
- int n = 5;
- Solution S1;
- std::vector<int> res = S1.countBits(n);
- for(auto num : res)
- std::cout << num << " ";
- std::cout << std::endl;
- return 0;
- }

主要思路:
维护一个小根堆,存储k个高频元素;
- #include
- #include
- #include
- #include
-
- class Solution{
- public:
- std::vector<int> topKFrequent(std::vector<int>& nums, int k){
- std::unordered_map<int, int> hash_map;
- for(int i = 0; i < nums.size(); i++){
- hash_map[nums[i]] += 1;
- }
-
- // 小顶堆
- std::priority_queue
int, int>, std::vectorint, int>>, mycompare> pri_q; - for(auto i = hash_map.begin(); i != hash_map.end(); i++){
- if(pri_q.size() >= k){
- if(i->second > pri_q.top().second){
- pri_q.pop();
- pri_q.emplace(i->first, i->second);
- }
- }
- else{
- pri_q.emplace(i->first, i->second);
- }
- }
-
- std::vector<int> res;
- while(!pri_q.empty()){
- res.push_back(pri_q.top().first);
- pri_q.pop();
- }
- return res;
- }
-
- class mycompare{
- public:
- bool operator()(std::pair<int, int>& item1, std::pair<int, int>& item2){
- return item1.second > item2.second;
- }
- };
- };
-
- int main(int argc, char argv[]){
- // nums = [1, 1, 1, 2, 2, 3], k = 2
- std::vector<int> test = {1, 1, 1, 2, 2, 3};
- int k = 2;
- Solution S1;
- std::vector<int> res = S1.topKFrequent(test, k);
- for(int num : res) std::cout << num << " ";
- std::cout << std::endl;
- return 0;
- }

主要思路:
从后开始遍历,用一个栈存储访问的字符,遇到左括号就处理字符串和重复次数;
- #include
- #include
- #include
-
- class Solution {
- public:
- std::string decodeString(std::string s) {
- std::stack
stk; - int i = s.length() - 1; // 从后开始遍历
- while(i >= 0){
- if(s[i] == ']' || ('a' <= s[i] && s[i] <= 'z')){
- stk.push(s.substr(i, 1));
- i--; // 移到下一个字符
- }
- else{ // 遇到 '['
- // 取出栈中的所有字符串(除']')
- std::string tmp;
- while(!stk.empty() && stk.top() != "]"){
- tmp += stk.top();
- stk.pop();
- }
- if(!stk.empty()) stk.pop(); // 弹出 "]"
-
- i--; // 移到数字位
- // 处理数字
- int val = 0, base = 1;
- while(i >= 0 && s[i] >= '0' && s[i] <= '9'){
- val += (s[i] - '0') * base;
- base = base * 10;
- i--;
- }
-
- // 存储字符串到栈中
- while(val > 0){
- stk.push(tmp);
- val--;
- }
- }
- }
- std::string res;
- // 将栈中的字符串取出
- while(!stk.empty()){
- res += stk.top();
- stk.pop();
- }
- return res;
- }
- };
-
- int main(int argc, char* argv[]){
- // s = "3[a]2[bc]"
- std::string test = "3[a]2[bc]";
- Solution S1;
- std::string res = S1.decodeString(test);
- std::cout << res << std::endl;
- return 0;
- }

主要思路:
基于带权重的并查集。I think this is a hard question....
- #include
- #include
- #include
- #include
-
- class Solution {
- public:
- int findf(std::vector<int>& f, std::vector<double>& w, int x) { // 引用传递,方便递归更新
- if (f[x] != x) { // 递归查找父亲节点
- int father = findf(f, w, f[x]);
- w[x] = w[x] * w[f[x]]; // 更新权重
- f[x] = father; // 更新父亲节点
- }
- return f[x];
- }
-
- void merge(std::vector<int>& f, std::vector<double>& w, int x, int y, double val) {
- int fx = findf(f, w, x); // 查找父亲节点
- int fy = findf(f, w, y); // 查找父亲节点
- f[fx] = fy; // 对于x节点的父亲节点,其父亲节点设置为y的父亲节点
- w[fx] = val * w[y] / w[x]; // 同时更新权重, x / y = val, 而 y/f[y] = w[y], x / f[x] = w[x] 则 f[x] / f[y] = val * w[y] / w[x]
- } // x / f[y] = val*w[y], x = f[x]*w[x] -> f[x]*w[x] / f[y] = val * w[y] -> f[x] / f[y] = val * w[y] / w[x]
-
- std::vector<double> calcEquation(std::vector
>& equations, std::vector<double>& values, std::vector>& queries) { - // 为每个字符节点记录其对应的id
- int nvars = 0;
- std::unordered_map
int> variables; - int n = equations.size();
- for (int i = 0; i < n; i++) {
- if (variables.find(equations[i][0]) == variables.end()) {
- variables[equations[i][0]] = nvars++;
- }
- if (variables.find(equations[i][1]) == variables.end()) {
- variables[equations[i][1]] = nvars++;
- }
- }
-
- std::vector<int> f(nvars);
- std::vector<double> w(nvars, 1.0);
- for (int i = 0; i < nvars; i++){ // 初始化字符节点的父亲节点
- f[i] = i;
- }
-
- for (int i = 0; i < n; i++) {
- int va = variables[equations[i][0]], vb = variables[equations[i][1]];
- merge(f, w, va, vb, values[i]); // 合并
- }
-
- std::vector<double> ret;
- for (const auto& q: queries) { // 遍历queries
- double result = -1.0;
- if (variables.find(q[0]) != variables.end() && variables.find(q[1]) != variables.end()) { // 同时存在
- int ia = variables[q[0]], ib = variables[q[1]]; // 取出对应的id
- int fa = findf(f, w, ia), fb = findf(f, w, ib); // 计算对应的父亲
- if (fa == fb) { // 具有相同的父亲,表明联通
- result = w[ia] / w[ib];
- }
- }
- ret.push_back(result);
- }
- return ret;
- }
- };
-
- int main(int argc, char* argv[]){
- // equations = [["a","b"],["b","c"]], values = [2.0,3.0], queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]]
- std::vector
> equations = {{"a", "b"}, {"b", "c"}}; - std::vector<double> values = {2.0, 3.0};
- std::vector
> queries = {{"a", "c"}, {"b", "a"}, {"a", "e"}, {"a", "a"}, {"x", "x"}}; - Solution S1;
- std::vector<double> res = S1.calcEquation(equations, values, queries);
- for(double num : res) std::cout << num << " ";
- std::cout << std::endl;
- return 0;
- }

主要思路:
对数组按身高从大到小排序,身高相同按k从小到大排序;
遍历数组,根据k来插入到结果数组中。
- #include
- #include
- #include
-
- class Solution {
- public:
- std::vector
int>> reconstructQueue(std::vectorint>>& people) { - std::sort(people.begin(), people.end(), [](std::vector<int> a, std::vector<int> b){
- if(a[0] == b[0]) return a[1] < b[1];
- return a[0] > b[0];
- });
- std::vector
int>> res; - for(int i = 0; i < people.size(); i++){
- int pos = people[i][1];
- res.insert(res.begin() + pos, people[i]);
- }
- return res;
- }
- };
-
- int main(int argc, char* argv[]){
- // people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
- std::vector
int>> test = {{7, 0}, {4, 4}, {7, 1}, {5, 0}, {6, 1}, {5, 2}}; - Solution S1;
- std::vector
int>> res = S1.reconstructQueue(test); - for(auto vec : res){
- for(int num : vec) std::cout << num << " ";
- std::cout << std::endl;
- }
- return 0;
- }

主要思路:
转化成0 - 1背包问题,一半的子集和vec作为背包容量,另一半的子集和作为物品,只需判断最后 dp[vec] 是否等于 vec 即可;
这道题使用 0-1 背包问题的 1 维解法时,需要先正序遍历物品,再倒序遍历背包,防止物品被多次选择(回看背包问题的笔记)。
- #include
- #include
- #include
-
- class Solution {
- public:
- bool canPartition(std::vector<int>& nums) {
- int sum = 0;
- for(int i = 0; i < nums.size(); i++){
- sum += nums[i];
- }
- if(sum % 2 != 0) return false;
-
- // 转化为背包问题
- int vec = int(sum / 2); // 背包容量
- std::vector<int> dp(vec+1, 0); // dp[i] 背包容量为i时装的物品价值
- dp[0] = 0;
- for(int j = 0; j < nums.size(); j++){ // 遍历物品
- for(int i = vec; i >= 0; i--){ // 遍历背包
- if(i >= nums[j]){
- dp[i] = std::max(dp[i - nums[j]] + nums[j], dp[i]);
- }
- }
- }
- if(dp[vec] == vec) return true;
- else return false;
- }
- };
-
- int main(int argc, char* argv[]){
- // nums = [1, 5, 11, 5]
- std::vector<int> test = {1, 5, 11, 5};
- Solution S1;
- bool res = S1.canPartition(test);
- if(res) std::cout << "true" << std::endl;
- else std::cout << "false" << std::endl;
- return 0;
- }

主要思路:
经典二叉树深搜,本题需要向下传递每一个节点的加和值(因为每一个节点都可以作为起始节点)。
- #include
- #include
-
- 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:
- int pathSum(TreeNode* root, int targetSum) {
- int res = 0, num = 0; // 结果
- dfs(root, targetSum, {}, res);
- return res;
- }
-
- void dfs(TreeNode* root, int targetSum, std::vector<long long> sum, int& res){
- if(root == nullptr) return;
- for(long long& num : sum) {
- if(num + root->val == targetSum){ // 上面的节点作为起始节点,以当前节点作为终点,匹配成功
- res++; // 结果+1
- }
- num += root->val; // 更新路径和:加上当前节点
- }
- if(root->val == targetSum) res++; // 判断单个当前节点是否匹配
- sum.push_back(root->val); // 更新路径和(当前节点可以作为新的起始节点)
- dfs(root->left, targetSum, sum, res); // 深搜
- dfs(root->right, targetSum, sum, res);
- return;
- }
- };
-
- int main(int argc, char* argv[]){
- // root = [10, 5, -3, 3, 2, null, 11, 3, -2, null, 1], targetSum = 8
- TreeNode *Node1 = new TreeNode(10);
- TreeNode *Node2 = new TreeNode(5);
- TreeNode *Node3 = new TreeNode(-3);
- TreeNode *Node4 = new TreeNode(3);
- TreeNode *Node5 = new TreeNode(2);
- TreeNode *Node6 = new TreeNode(11);
- TreeNode *Node7 = new TreeNode(3);
- TreeNode *Node8 = new TreeNode(-2);
- TreeNode *Node9 = new TreeNode(1);
-
- Node1->left = Node2;
- Node1->right = Node3;
- Node2->left = Node4;
- Node2->right = Node5;
- Node3->right = Node6;
- Node4->left = Node7;
- Node4->right = Node8;
- Node5->right = Node9;
-
- int targetSum = 8;
-
- Solution S1;
- int res = S1.pathSum(Node1, targetSum);
- std::cout << res << std::endl;
-
- return 0;
- }

主要思路:
基于滑动窗口,判断s串滑动窗口的元素是否与p串匹配;
- #include
- #include
-
- class Solution {
- public:
-
- bool check(std::vector<int> cnts, std::vector<int> cntp){
- for(int i = 0; i < 26; i++){
- if(cnts[i] != cntp[i]) return false;
- }
- return true;
- }
-
- std::vector<int> findAnagrams(std::string s, std::string p) {
- // 基于滑动窗口
- // 先统计p串各字符出现的次数
- std::vector<int> cntp(26);
- for(int i = 0; i < p.length(); i++){
- cntp[p[i] - 'a']++;
- }
-
- // 初始化s串第一个滑动窗口各字符出现的次数
- std::vector<int> cnts(26);
- for(int i = 0; i < std::min(p.length(), s.length()); i++){ // s串可能会小于p串
- cnts[s[i] - 'a']++;
- }
-
- std::vector<int> res;
- if(check(cnts, cntp)) res.push_back(0); // 判断第一个窗口是否合法
-
- for(int r = p.length(); r < s.length(); r++){ // 枚举右边界
- int l = r - p.length() + 1; // 当前窗口左边界
- cnts[s[l-1] - 'a']--; // 移除上一个窗口左边界元素
- cnts[s[r] - 'a']++; // 移入右边界元素
- if(check(cnts, cntp)) res.push_back(l); // 匹配,记录当前窗口的左边界索引
- }
- return res;
- }
- };
-
- int main(int argc, char* argv[]){
- // s = "cbaebabacd", p = "abc"
- std::string s = "cbaebabacd";
- std::string p = "abc";
- Solution S1;
- std::vector<int> res = S1.findAnagrams(s, p);
- for(int num : res) std::cout << num << " ";
- std::cout << std::endl;
- return 0;
- }

主要思路:
原地修改数组,遍历数组,根据数组的值修改对应原数组索引的值,具体对应关系是 1对应坐标0,2对应坐标1;修改数组时,将对应坐标的值加上 nums.size(),最后只需判断哪个坐标的值 <= nums.size(),就可以知道哪些值没有出现过。
- #include
- #include
-
- class Solution {
- public:
- std::vector<int> findDisappearedNumbers(std::vector<int>& nums) {
- std::vector<int> res;
- for(int i = 0; i < nums.size(); i++){
- int pos = (nums[i] - 1) % nums.size();
- nums[pos] += nums.size();
- }
- for(int i = 0; i < nums.size(); i++){
- if (nums[i] <= nums.size()) res.push_back(i+1);
- }
- return res;
- }
- };
-
- int main(int argc, char* argv[]){
- // nums = [4, 3, 2, 7, 8, 2, 3, 1]
- std::vector<int> test = {4, 3, 2, 7, 8, 2, 3, 1};
- Solution S1;
- std::vector<int> res = S1.findDisappearedNumbers(test);
- for(int num : res) std::cout << num << " ";
- std::cout << std::endl;
- return 0;
- }