leetcode解题思路分析(二十)134 - 140题

淡淡的烟草味﹌ 2023-07-22 03:43 127阅读 0赞
  1. 加油站
    在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
    你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
    如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
    说明:
    如果题目有解,该答案即为唯一答案。
    输入数组均为非空数组,且长度相同。
    输入数组中的元素均为非负数。

算法

  1. 初始化 total_tank 和 curr_tank 为 0 ,并且选择 0 号加油站为起点。
  2. 遍历所有的加油站:
    a. 每一步中,都通过加上 gas[i] 和减去 cost[i] 来更新 total_tank 和 curr_tank 。
    b. 如果在 i + 1 号加油站, curr_tank < 0 ,将 i + 1 号加油站作为新的起点,同时重置 curr_tank = 0 ,让油箱也清空。
  3. 如果 total_tank < 0 ,返回 -1 ,否则返回 starting station。

想象 total_tank >= 0 的情况,同时上述算法返回 Ns作为出发加油站。算法直接保证了从 Ns 可以到达 0 ,但是剩余的路程,即从 0 到站 Ns是否有足够的油呢?如何确保从 Ns 出发可以环行一圈?我们使用 反证法可解。

  1. class Solution {
  2. public:
  3. int canCompleteCircuit(vector<int>& gas, vector<int>& cost)
  4. {
  5. int n = gas.size();
  6. int total_tank = 0;
  7. int curr_tank = 0;
  8. int starting_station = 0;
  9. for (int i = 0; i < n; ++i)
  10. {
  11. total_tank += gas[i] - cost[i];
  12. curr_tank += gas[i] - cost[i];
  13. // If one couldn't get here,
  14. if (curr_tank < 0)
  15. {
  16. // Pick up the next station as the starting one.
  17. starting_station = i + 1;
  18. // Start with an empty tank.
  19. curr_tank = 0;
  20. }
  21. }
  22. return total_tank >= 0 ? starting_station : -1;
  23. }
  24. };
  1. 分发糖果
    老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
    你需要按照以下要求,帮助老师给这些孩子分发糖果:
    每个孩子至少分配到 1 个糖果。
    相邻的孩子中,评分高的孩子必须获得更多的糖果。
    那么这样下来,老师至少需要准备多少颗糖果呢?

本题类似于接雨水:需要保证左右两边均满足条件,因此需要从左往右和从右往左各遍历一遍

  1. class Solution {
  2. public:
  3. int candy(vector<int>& ratings)
  4. {
  5. const int N = ratings.size();
  6. if(N == 0) return 0;
  7. if(N == 1) return 1;
  8. vector<int> left(N, 0);
  9. left[0] = 1;
  10. for(int i = 1; i < N; i++)
  11. {
  12. if(ratings[i] > ratings[i - 1])
  13. {
  14. left[i] = left[i - 1] + 1;
  15. }
  16. else if(ratings[i] <= ratings[i - 1])
  17. {
  18. left[i] = 1;
  19. }
  20. }
  21. int right = 1;
  22. int s = 0;
  23. s += max(left[N - 1],right);
  24. for(int i = N - 2; i >= 0; i--)
  25. {
  26. if(ratings[i] > ratings[i + 1])
  27. {
  28. right = right + 1;
  29. }
  30. else if(ratings[i] <= ratings[i + 1])
  31. {
  32. right = 1;
  33. }
  34. s += max(left[i], right);
  35. }
  36. return s;
  37. }
  38. };
  1. 只出现一次的数字
    给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

线性时间复杂度 + 不能使用额外空间,所以不能用哈希表辅助解决,这里比较适合的解法是用异或做位运算。异或的特性在于同一个元素两次异或为0,0异或其他为该元素本身,因此遍历异或一遍即可

  1. class Solution {
  2. public:
  3. int singleNumber(vector<int>& nums) {
  4. int v=0;
  5. for(int i=0; i<nums.size(); i++){
  6. v ^= nums[i];
  7. }
  8. return v;
  9. }
  10. };
  1. 只出现一次的数字2
    给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

如果其他数都出现了 kk 次,一个数出现了一次。那么如果 kk 是偶数,还是把所有的数异或起来就行了。如果 kk 是奇数,那么统计每一位是 1 的个数,然后模 kk 取余数就能得到那个单独的数了。

  1. class Solution {
  2. public:
  3. int singleNumber(vector<int>& nums) {
  4. int once = 0, twice = 0;
  5. for (auto x : nums) {
  6. once = (once^x)&(~twice);
  7. twice = (twice^x)&(~once);
  8. }
  9. return once;
  10. }
  11. };
  1. 复制带随机指针的链表
    给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。
    要求返回这个链表的 深拷贝。

解题思路:1.采取哈希表;2. 原地克隆再分离:复制节点,同时将复制节点链接到原节点后面,如A->B->C 变为 A->A’->B->B’->C->C’。设置节点random值。将复制链表从原链表分离

  1. /* // Definition for a Node. class Node { public: int val; Node* next; Node* random; Node(int _val) { val = _val; next = NULL; random = NULL; } }; */
  2. class Solution {
  3. public:
  4. Node* copyRandomList(Node* head) {
  5. if (head == nullptr) {
  6. return head;
  7. }
  8. Node *node = head;
  9. //1. 将复制节点添加到原节点后面
  10. while (node != nullptr) {
  11. Node *copy = new Node(node->val, nullptr, nullptr);
  12. copy->next = node->next;
  13. node->next = copy;
  14. node = copy->next;
  15. }
  16. //2. 复制random节点
  17. node = head;
  18. while (node != nullptr) {
  19. if (node->random != nullptr) {
  20. node->next->random = node->random->next;
  21. }
  22. node = node->next->next;
  23. }
  24. //3. 分离链表
  25. node = head;
  26. Node *newHead = head->next;
  27. Node *newNode = newHead;
  28. while (node != nullptr) {
  29. node->next = node->next->next;
  30. if (newNode->next != nullptr) {
  31. newNode->next = newNode->next->next;
  32. }
  33. node = node->next;
  34. newNode = newNode->next;
  35. }
  36. return newHead;
  37. }
  38. };
  1. 单词拆分
    给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

如果字符串很大,字典很大,采用Trie树或者AC自动机才是最优解,但是这里最好的选择是采用动态规划

  1. class Solution
  2. {
  3. public:
  4. bool wordBreak(string s, vector<string>& wordDict)
  5. {
  6. set<string> words;
  7. for(int i = 0; i < wordDict.size(); i++)
  8. {
  9. words.insert(wordDict[i]);
  10. }
  11. vector<bool> dp(s.length() + 1);
  12. dp[0] = true;
  13. for (int i = 1; i <= s.length(); i++)
  14. {
  15. for (int j = 0; j < i; j++) {
  16. if (dp[j] && words.find(s.substr(j, i - j)) != words.end() )
  17. {
  18. dp[i] = true;
  19. break;
  20. }
  21. }
  22. }
  23. return dp[s.length()];
  24. }
  25. };
  1. 单词拆分2
    给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的句子。

本题和上题基本类似,但是需要注意先检测是否存在可能性,然后再挨个存储,不然会时空超限制

  1. class Solution {
  2. public:
  3. vector<string> wordBreak(string s, vector<string>& wordDict)
  4. {
  5. if (!wordBreak_139(s, wordDict)) return { };
  6. size_t validEnd = 0;
  7. vector<vector<string>> dp(s.size() + 1, vector<string>());
  8. for (size_t i = 0; i < s.size(); i++)
  9. {
  10. if (i == validEnd + 1) return { };
  11. if (i != 0 && dp[i].empty()) continue;
  12. for (auto& word : wordDict)
  13. {
  14. size_t newEnd = i + word.size();
  15. if (newEnd > s.size()) continue;
  16. if (memcmp(&s[i], &word[0], word.size()) != 0) continue;
  17. validEnd = max(validEnd, newEnd);
  18. if (i == 0)
  19. {
  20. dp[newEnd].push_back(word);
  21. continue;
  22. }
  23. for (auto& d : dp[i])
  24. {
  25. dp[newEnd].push_back(d + " " + word);
  26. }
  27. }
  28. }
  29. return dp.back();
  30. }
  31. bool wordBreak_139(string& s, vector<string>& wordDict)
  32. {
  33. size_t validEnd = 0;
  34. vector<bool> dp(s.size() + 1, false);
  35. dp[0] = true;
  36. for (size_t i = 0; i < s.size(); i++)
  37. {
  38. if (i == validEnd + 1) return false;
  39. if (!dp[i]) continue;
  40. for (auto& word : wordDict)
  41. {
  42. size_t newEnd = i + word.size();
  43. if (newEnd > s.size()) continue;
  44. if (memcmp(&s[i], &word[0], word.size()) == 0)
  45. {
  46. dp[newEnd] = true;
  47. validEnd = max(validEnd, newEnd);
  48. }
  49. }
  50. }
  51. return dp.back();
  52. }
  53. };

发表评论

表情:
评论列表 (有 0 条评论,127人围观)

还没有评论,来说两句吧...

相关阅读