LeetCode 刷题记录——从零开始记录自己一些不会的

文章介绍了多个编程题目,涉及城堡攻防、路径计算、字符串操作、数据结构(如字典树)、动态规划、贪婪算法、正则表达式匹配、图论等问题,展示了如何在不同场景下使用IT技术解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 最多可以摧毁的敌人城堡数目

  • 题意

image-20230902220112381

  • 思路

两层循环,太low了

用一个变量记录前一个位置

  • 代码
class Solution {
public:
    int captureForts(vector<int>& forts) {
        int ans = 0, pre = -1;
        for (int i = 0; i < forts.size(); i++) {
            if (forts[i] == 1 || forts[i] == -1) {
                if (pre >= 0 && forts[i] != forts[pre]) {
                    ans = max(ans, i - pre - 1);
                }
                pre = i;
            }
        }
        return ans;
    }
};

2. 到达终点的数字

  • 题意
    在这里插入图片描述

  • 思路

754-2.png

image-20230902222848338

  • 代码
class Solution {
public:
    int reachNumber(int target) {
        target = abs(target);
        int s = 0, n = 0;
        while (s < target || (s - target) % 2) // 没有到达(越过)终点,或者相距奇数
            s += ++n;
        return n;
    }
};

3. 单词的压缩编码

  • 题意

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 思路

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 代码
class Solution {
public:
    int minimumLengthEncoding(vector<string>& words) {
        unordered_set<string> good(words.begin(), words.end());
        for (const string& word: words) {
            for (int k = 1; k < word.size(); ++k) {
                good.erase(word.substr(k));
            }
        }

        int ans = 0;
        for (const string& word: good) {
            ans += word.size() + 1;
        }
        return ans;
    }
};
  • 思路2

去找到是否不同的单词具有相同的后缀,我们可以将其反序之后插入字典树中。例如,我们有 “time” 和 “me”,可以将 “emit” 和 “em” 插入字典树中。

fig2

然后,字典树的叶子节点(没有孩子的节点)就代表没有后缀的单词,统计叶子节点代表的单词长度加一的和即为我们要的答案。

  • 代码
class TrieNode{
    TrieNode* children[27];
    public:
    int count;
    TrieNode() {
        for (int i = 0;i < 26;i ++) 
            children[i] = NULL;
        count = 0;
    }
    TrieNode* get(char c) {
        if (children[c-'a'] == NULL) {
            children[c-'a'] = new TrieNode();
            count ++;
        }
        return children[c-'a'];
    }
};
class Solution{
    public:
    int minimumLengthEncoding(vector<string>& words) {
        TrieNode* trie = new TrieNode();
        unordered_map<TrieNode*,int> nodes;
        for (int i = 0;i < (int)words.size();i ++) {
            string word = words[i];
            TrieNode* cur = trie;
            for (int j = word.length()-1;j >= 0;j --) 
                cur = cur->get(word[j]);
            nodes[cur] = i;
        }
        int ans = 0;
        for (auto& [node,idx] : nodes) {
            if (node->count == 0) {
                ans += words[idx].length() + 1;
            }
        }
        return ans;
    }
};

字典树

4. 逃脱阻碍着

  • 题意

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 思路

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

另:

image-20230903121017850

  • 代码
class Solution {
public:
    int manhattanDistance(vector<int>& point1, vector<int>& point2) {
        return abs(point1[0] - point2[0]) + abs(point1[1] - point2[1]);
    }

    bool escapeGhosts(vector<vector<int>>& ghosts, vector<int>& target) {
        vector<int> source(2);
        int distance = manhattanDistance(source, target);
        for (auto& ghost : ghosts) {
            int ghostDistance = manhattanDistance(ghost, target);
            if (ghostDistance <= distance) {
                return false;
            }
        }
        return true;
    }
};

5. 寻找两个正序数组的中位数 O ( l o g ( m + n ) ) O(log(m+n)) O(log(m+n))

  • 题意

image-20230831105355390

  • 思路

主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较

这里的 “/” 表示整除
s1 中小于等于 pivot1 的元素有 nums1[0 … k/2-2] 共计 k/2-1 个

  • nums2 中小于等于 pivot2 的元素有 nums2[0 … k/2-2] 共计 k/2-1 个
    ivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
  • 这样 pivot 本身最大也只能是第 k-1 小的元素
    pivot = pivot1,那么 nums1[0 … k/2-1] 都不可能是第 k 小的元素。把这些元素全部 “删除”,剩下的作为新的 nums1 数组
  • 如果 pivot = pivot2,那么 nums2[0 … k/2-1] 都不可能是第 k 小的元素。把这些元素全部 “删除”,剩下的作为新的 nums2 数组
    们 “删除” 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
  • 代码
class Solution {
public:
    int getKthElement(const vector<int>& nums1, const vector<int>& nums2, int k) {
        int m = nums1.size();
        int n = nums2.size();
        int index1 = 0, index2 = 0;

        while (true) {
            // 边界情况
            if (index1 == m) {
                return nums2[index2 + k - 1];
            }
            if (index2 == n) {
                return nums1[index1 + k - 1];
            }
            if (k == 1) {
                return min(nums1[index1], nums2[index2]);
            }
            // 正常情况
            int newIndex1 = min(index1 + k / 2 - 1, m - 1);
            int newIndex2 = min(index2 + k / 2 - 1, n - 1);
            int pivot1 = nums1[newIndex1];
            int pivot2 = nums2[newIndex2];
            if (pivot1 <= pivot2) {
                k -= newIndex1 - index1 + 1;
                index1 = newIndex1 + 1;
            }
            else {
                k -= newIndex2 - index2 + 1;
                index2 = newIndex2 + 1;
            }
        }
    }
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int totalLength = nums1.size() + nums2.size();
        if (totalLength % 2 == 1) {
            return getKthElement(nums1, nums2, (totalLength + 1) / 2);
        }
        else {
            return (getKthElement(nums1, nums2, totalLength / 2) + getKthElement(nums1, nums2, totalLength / 2 + 1)) / 2.0;
        }
    }
};

6. 正则表达式匹配

  • 题意

image-20230831142952316

  • 思路

点号通配符其实很好实现,s 中的任何字符,只要遇到 . 通配符,无脑匹配就完事了。主要是这个星号通配符不好实现,一旦遇到 * 通配符,前面的那个字符可以选择重复一次,可以重复多次,也可以一次都不出现,这该怎么办?

对于这个问题,答案很简单,对于所有可能出现的情况,全部穷举一遍,只要有一种情况可以完成匹配,就认为 p 可以匹配 s。那么一旦涉及两个字符串的穷举,我们就应该条件反射地想到动态规划的技巧了。

  • 代码

待补

7. 一个图中联通三元组的最小度数

  • 题意

image-20230831143639125

待补

8. 只出现一次的数字Ⅱ 位运算

  • 题意

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 **三次 。**请你找出并返回那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。

  • 思路

image-20230831151525370

  • 代码
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ans = 0;
        for (int i = 0; i < 32; ++i) {
            int total = 0;
            for (int num: nums) {
                total += ((num >> i) & 1);
            }
            if (total % 3) {
                ans |= (1 << i);
            }
        }
        return ans;
    }
};

9. 数字范围按位与

  • 题意

给你两个整数 leftright ,表示区间 [left, right] ,返回此区间内所有数字 按位与 的结果(包含 leftright 端点)。

  • 思路

m和n的二进制串的最长公共前缀

image.png

  • 代码
class Solution {
public:
    int rangeBitwiseAnd(int m, int n) {
        while (m < n) {
            // 抹去最右边的 1
            n = n & (n - 1);
        }
        return n;
    }
};
Brian Kernighan 算法

10. 阶乘后的零

  • 题意

image-20230831155540328

  • 思路

在这里插入图片描述

  • 代码
class Solution {
public:
    int trailingZeroes(int n) {
        int ans = 0;
        while (n) {
            n /= 5;
            ans += n;
        }
        return ans;
    }
};

11. 最深叶节点的最近公共祖先

  • 题意

给你一个有根节点 root 的二叉树,返回它最深的叶节点的最近公共祖先。

在这里插入图片描述

image-20230906224129121

  • 思路

在这里插入图片描述

  • 代码
/**
 * 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:
    pair<TreeNode*,int> f(TreeNode* root) {
        if (!root) {
            return {root,0};
        }

        auto left = f(root->left);
        auto right = f(root->right);

        if (left.second > right.second) {
            return {left.first,left.second+1};
        }

        if (left.second < right.second) {
            return {right.first,right.second+1};
        }

        return {root,left.second+1};
    }
    TreeNode* lcaDeepestLeaves(TreeNode* root) {
        return f(root).first;
    }
};

12. 连续整数求和

  • 题意

在这里插入图片描述

  • 思路

image-20230907091741158

  • 代码
class Solution {
public:
    int consecutiveNumbersSum(int n) {
        int ans = 0;
        int bound = 2 * n;
        for (int k = 1; k * (k + 1) <= bound; k++) {
            if (isKConsecutive(n, k)) {
                ans++;
            }
        }
        return ans;
    }
  
    bool isKConsecutive(int n, int k) {
        if (k % 2 == 1) {
            return n % k == 0;
        } else {
            return n % k != 0 && 2 * n % k == 0;
        }
    }
};

13. 构造最长非递减子数组

  • 题意

在这里插入图片描述

  • 思路

动态规划

  • 代码
class Solution {
public:
    int maxNonDecreasingLength(vector<int>& nums1, vector<int>& nums2) {
        int ans = 1, n = nums1.size();
        vector<vector<int>> dp(n, vector<int>(2, 1));
        for (int i = 1; i < n; ++i) {
            if (nums1[i] >= nums1[i-1]) dp[i][0] = dp[i-1][0] + 1;
            if (nums1[i] >= nums2[i-1]) dp[i][0] = max(dp[i][0], dp[i-1][1] + 1);
            if (nums2[i] >= nums1[i-1]) dp[i][1] = dp[i-1][0] + 1;
            if (nums2[i] >= nums2[i-1]) dp[i][1] = max(dp[i][1], dp[i-1][1] + 1);
            ans = max(ans, max(dp[i][0], dp[i][1]));
        }
        return ans;
    }
};

14. 等值距离和

  • 题意

在这里插入图片描述

  • 思路

分组+前缀和

  • 代码
class Solution {
public:
    vector<long long> distance(vector<int> &nums) {
        int n = nums.size();
        unordered_map<int, vector<int>> groups;
        for (int i = 0; i < n; ++i)
            groups[nums[i]].push_back(i); // 相同元素分到同一组,记录下标

        vector<long long> ans(n);
        long long s[n + 1];
        s[0] = 0;
        for (auto &[_, a]: groups) {
            int m = a.size();
            for (int i = 0; i < m; ++i)
                s[i + 1] = s[i] + a[i]; // 前缀和
            for (int i = 0; i < m; ++i) {
                long long target = a[i];
                long long left = target * i - s[i]; // 蓝色面积
                long long right = s[m] - s[i] - target * (m - i); // 绿色面积
                ans[target] = left + right;
            }
        }
        return ans;
    }
};

15. 使数组元素全部相等的最少操作次数

  • 题意

image-20230907104451436

  • 思路

t3.png

  • 代码
class Solution {
public:
    vector<long long> minOperations(vector<int> &nums, vector<int> &queries) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        long long sum[n + 1]; // 前缀和
        sum[0] = 0;
        for (int i = 0; i < n; ++i)
            sum[i + 1] = sum[i] + nums[i];

        int m = queries.size();
        vector<long long> ans(m);
        for (int i = 0; i < m; ++i) {
            int q = queries[i];
            long long j = lower_bound(nums.begin(), nums.end(), q) - nums.begin();
            long long left = q * j - sum[j]; // 蓝色面积
            long long right = sum[n] - sum[j] - q * (n - j); // 绿色面积
            ans[i] = left + right;
        }
        return ans;
    }
};

16. 课程表Ⅲ

  • 题意

image-20230911202337410

  • 思路
    IMAGE
    ![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fcdn.jsdelivr.net%2Fgh%2Fharder-er%2Fblogimage%40main%2Fimgimage-20230911202337410.png&pos_id=img-SARb3m5Q-2023091120233710.png46image-20230911202554983

  • 代码

class Solution {
public:
    int scheduleCourse(vector<vector<int>>& courses) {
        sort(courses.begin(),courses.end(),[](const auto& c0,const auto& c1){
            return c0[1] < c1[1];
        });
        priority_queue<int> q;

        int total = 0;
        for (const auto & courses: courses) {
            int ti = courses[0],di = courses[1];
            if (total + ti <= di) {
                total += ti;
                q.push(ti);
            } else if (!q.empty() && q.top() > ti) {
                total -= q.top() - ti;
                q.pop();
                q.push(ti);
            }
        }
        return q.size();
    }
};

17. 安排工作以达到最大收益

  • 题意

image-20230911205637991

  • 思路

我们可以以任意顺序考虑工人,所以我们按照能力大小排序。

如果我们先访问低难度的工作,那么收益一定是截至目前最好的。

算法

我们使用 “双指针” 的方法去安排任务。我们记录最大可用利润 best。对于每个能力值为 skill 的工人,找到难度小于等于能力值的任务,并将如结果中。

  • 代码
  1. 排序+线性查找

    class Solution {
    public:
        int maxProfitAssignment(vector<int>& difficulty, vector<int>& profit, vector<int>& worker) {
            const int n = (int)difficulty.size();
            vector<pair<int,int>>jobs(n);
    
            for (int i = 0;i < n;i ++) {
                jobs[i] = make_pair(difficulty[i],profit[i]);
            }
            sort(jobs.begin(),jobs.end());
            sort(worker.begin(),worker.end());
            int ans = 0;
            int idx = 0;int best = 0;
            for (int level:worker) {
                while (idx < n&&level >= jobs[idx].first) {
                    best = max (best,jobs[idx++].second);
                }
                ans += best;
            }
            return ans;
        }
    };
    
    1. 排序+预处理+二分查找
    class Solution {
    public:
        int maxProfitAssignment(vector<int>& difficulty, vector<int>& profit, vector<int>& worker) {
            const int n = (int)difficulty.size();
            vector<pair<int,int>>jobs(n);
    
            for (int i = 0;i < n;i ++) {
                jobs[i] = make_pair(difficulty[i],profit[i]);
            }
            sort(jobs.begin(),jobs.end());
            int maxPofit = 0;
            for (int i = 0;i < n;i ++) {
                jobs[i].second = max(maxPofit,jobs[i].second);
                maxPofit = jobs[i].second;
            }
            
            sort(worker.begin(),worker.end());
            int ans = 0;
            int idx = 0;int best = 0;
            for (int i = 0;i < worker.size();i ++) {
                int l = -1, r = n;
                while (l + 1 != r) {
                    int mid = (l + r)>>1;
                    if (jobs[mid].first <= worker[i]) {
                        l = mid;
                    } else {
                        r = mid;
                    }
                }
                if (l != -1) {
                    best = max(best,jobs[l].second);
                    ans += best;
                }
            }
            return ans;
        }
    };
    

18. 子串能表示从 1 到 N 数字的二进制串

  • 题意

IMAGE

  • 思路

image-20230911214210252

  • 代码
class Solution {
public:
    bool help(const string& s,int k,int mi,int ma) {
        unordered_set<int> st;
        int t = 0;
        for (int r = 0;r < s.size();r ++) {
            t = t*2+(s[r] - '0');
            if (r >= k) {
                t -= (s[r-k]-'0')<<k;
            }
            if (r >= k-1&&t >= mi&&t <= ma) {
                st.insert(t);
            }
        }
        return st.size() == ma-mi+1;
    }
    bool queryString(string s, int n) {
        if (n == 1) return s.find('1') != -1;
        int k = 30;
        while ((1<<k) > n) {
            -- k;
        }
        if (s.size() < (1 <<(k-1))+k-1||s.size() < n-(1<<k)+k+1) return false;
        return help(s,k,1<<(k-1),(1<<k)-1)&&help(s,k+1,1<<k,n);
    }
};

19. 最大连续1的个数Ⅱ

  • 题意

给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k0 ,则返回 数组中连续 1 的最大个数

  • 思路

动态规划,no,贪心:找到每个‘1’块之间的距离,加起来。

image-20230912094314627

  • 方法一

    image-20230912094442540

image-20230912094458234

  • 代码
class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> P(n+1);
        for (int i= 1;i <= n;i ++) {
            P[i] = P[i-1] + (1-nums[i-1]);//1变成0,0变成1
        }

        int ans = 0;
        for (int right = 0;right < n; right ++) {
            int left = lower_bound(P.begin(),P.end(),P[right+1]-k)-P.begin();
            ans = max(ans,right - left + 1);
        }
        return ans;
    }
};
  • 方法二

image-20230912095201308

  • 代码
class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int n = nums.size();
        int left = 0,lsum = 0,rsum = 0;
        int ans = 0;
        for (int right = 0;right < n;right ++) {
            rsum += 1-nums[right];
            while (lsum < rsum - k) {
                lsum += 1-nums[left];
                ++ left;
            }
            ans = max(ans,right - left + 1);
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯狂的码泰君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值