LeetCode每日一题打卡开始于2021-05-14,预计打卡至大四毕业,总共两年多,每天都会打卡,但不一定会更新到这上面。我不想再当咸鱼了(逃。
2021-05-14,数电课延后一周再上,上完我就要开始学了。
方法一:模拟
思路
根据罗马数字的唯一表示法,为了表示一个给定的整数num ,我们寻找不超过num 的最大符号值,将num 减去该符号值,然后继续寻找不超过num 的最大符号值,将该符号拼接在上一个找到的符号之后,循环直至num 为0。最后的得到的字符即为num 的罗马数字表示。
编程时,可以建立一个数值-符号对的列表valueSymbols ,按数值从大到小排列。遍历valueSymbols 中的每个数值-符号对,若当前数值value 不超过num 则从num 中不断减去value ,直至num 小于value ,然后遍历下一个数值-符号对。若遍历中num 为0则跳出循环。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const pair<int , string> valueSymbols[] = { {1000 , "M" }, {900 , "CM" }, {500 , "D" }, {400 , "CD" }, {100 , "C" }, {90 , "XC" }, {50 , "L" }, {40 , "XL" }, {10 , "X" }, {9 , "IX" }, {5 , "V" }, {4 , "IV" }, {1 , "I" }, }; class Solution {public : string intToRoman (int num) { string roman; for (const auto &[value, symbol] : valueSymbols){ while (num>=value){ num -= value; roman += symbol; } if (num == 0 ) break ; } return roman; } };
复杂度分析
今天是2021-05-15,周六,可是为什么还有实验课啊,滑动变阻器坏了,实验没做出来,躺平~
方法一:模拟
思路
通常情况下,罗马数字中小的数字在大的数字的右边。若输入的字符串满足该情况,那么可以将每个字符视作一个单独的值,累加每个字符对应的数值即可。
例如XXVII可视作X + X+ V+ I+ I = 10 + 10 + 5 + 1 + 1 = 27。
若存在小的数字在大的数字的左边的情况,根据规则需要减去小的数字。对于这种情况,我们也可以将每个字符视作一个单独的值,若一个数字的右侧的数字比它大,则该数字的符号取反。
例如XIV可视作 X - I + V = 10 - 1 + 5 = 14。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution {public : int romanToInt (string s) { unordered_map<char , int > symbolValues = { {'I' , 1 }, {'V' , 5 }, {'X' , 10 }, {'L' , 50 }, {'C' , 100 }, {'D' , 500 }, {'M' , 1000 }, }; int ans = 0 ; int n = s.length (); for (int i=0 ; i < n; i++){ int value = symbolValues[s[i]]; if (i<n-1 && value < symbolValues[s[i+1 ]]) ans -= value; else ans += value; } return ans; } };
复杂度分析
时间复杂度:O (n )。其中n是字符串s的长度。
空间复杂度:O (1 )。
2021-05-16,周日,玩了一上午的OW(破坏球真好玩)。
方法一:暴力
题目要求在O (n)的时间复杂度下完成,但暴力解法时间复杂度会上升到O (n^2),用C++就会超时,而Java却AC了(不是很懂.jpg)。
代码
1 2 3 4 5 6 7 8 9 10 11 class Solution { public static int findMaximumXOR (int [] nums) { int ret = 0 ; for (int i = 0 ; i < nums.length; i++) { for (int j = i; j < nums.length; j++) { ret = Math.max(nums[i]^nums[j], ret); } } return ret; } }
方法二:哈希表
这个方法来自官方题解,自己想不出别的了。
思路
设最大异或值为m。[从高到低依次求m的每一个bit]
设x(k)表示x的从高到低的前k位。
如何求m?已经求出m(k-1),判断第k是否为1:设m第k位可以为1的条件是:存在x和y于nums,满足m(k)=x(k)^y(k),否则m第k位是0。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution {public : int findMaximumXOR (vector<int >& nums) { int m = 0 ; for (int k=30 ; k>=0 ; --k){ unordered_set<int > set; for (int num: nums){ set.insert (num>>k); } m = (m<<1 )+1 ; bool canBe1 = false ; for (int num: nums){ if (set.count ((num>>k)^m)){ canBe1 = true ; break ; } } if (!canBe1) --m; } return m; } };
2021-05-17,打卡第4天,上午有乒乓球课,玩得很过瘾啦~
前言
要想判断两个节点 x和 y 是否为堂兄弟节点,我们就需要求出这两个节点分别的「深度」以及「父节点」。
因此,我们可以从根节点开始,对树进行一次遍历,在遍历的过程中维护「深度」以及「父节点」这两个信息。当我们遍历到 x 或 y 节点时,就将信息记录下来;当这两个节点都遍历完成了以后,我们就可以退出遍历的过程,判断它们是否为堂兄弟节点了。
常见的遍历方法有两种:深度优先搜索和广度优先搜索。
方法一:深度优先搜索
思路与算法
我们只需要在深度优先搜索的递归函数中增加表示「深度」以及「父节点」的两个参数即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class Solution {private : int x; TreeNode* x_parent; int x_depth; bool x_found = false ; int y; TreeNode* y_parent; int y_depth; bool y_found = false ; public : bool isCousins (TreeNode*& root, int & x, int & y) { this ->x = x; this ->y = y; dfs (root, 0 ,nullptr ); return x_depth ==y_depth && x_parent != y_parent; } void dfs (TreeNode* node, int depth, TreeNode* parent) { if (node==nullptr ) return ; if (node->val == x) tie (x_parent, x_depth, x_found) = tuple (parent, depth, true ); else if (node->val == y) tie (y_parent, y_depth, y_found) = tuple (parent, depth, true ); if (x_found && y_found) return ; dfs (node->left, depth+1 , node); if (x_found && y_found) return ; dfs (node->right, depth+1 , node); } };
复杂度分析
时间复杂度:O(n),其中 n 是树中的节点个数。在最坏情况下,我们需要遍历整棵树,时间复杂度为 O(n)。
空间复杂度:O(n),即为深度优先搜索的过程中需要使用的栈空间。在最坏情况下,树呈现链状结构,递归的深度为 O(n)。
方法二:广度优先搜索
思路来自Qian
因为在BFS中,我们使用的是层序遍历,如果每次遍历一层,那么这一层的元素的深度是相同的。
因此我们在每一层,看看是否有出现 x 和 y,其中分为以下三种情况:
众所周知,BFS需要用到队列,那么我们应该如何设计队列的数据类型? 在这里,我采用了 pair<TreeNode*, TreeNode*>(其实pair<TreeNode*, int>也可以),其中pair中,第一个元素记录指向当前结点的指针,第二个元素记录指向当前结点的父结点的指针,这样就可以完美应对上面所说的三种情况了。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Solution {public : using PTT = pair<TreeNode*, TreeNode*>; bool isCousins (TreeNode* root, int x, int y) { queue<PTT> q; q.push ({root, nullptr }); while (!q.empty ()){ int n = q.size (); vector<TreeNode*> rec_parent; for (int i = 0 ; i < n; i++){ auto [cur, parent] = q.front (); q.pop (); if (cur->val == x || cur->val == y) rec_parent.push_back (parent); if (cur->left) q.push ({cur->left, cur}); if (cur->right) q.push ({cur->right, cur}); } if (rec_parent.size () == 0 ) continue ; else if (rec_parent.size () == 1 ) return false ; else if (rec_parent.size () == 2 ) return rec_parent[0 ] != rec_parent[1 ]; } return false ; } };
复杂度分析
2021-05-18,轻松的一天,只有一节德语课。
前言
记S(n) , n=0,1,2,3 $$ S(0) = 0, S(1) = arr[0], S(2) = arr[0] ⊕ arr[1], S(n) = arr[0] ⊕ arr[1] ⊕ … ⊕ arr[n-1] $$ 为该数组的异或前缀和 ,由异或的性质可知: $$ a = S(i-1) ⊕ S(j-1) = arr[i] ⊕ arr[i+1] ⊕ arr[i+2] ⊕ … ⊕ arr[j-1] $$
$$ b = S(j-1) ⊕ S(k) = arr[j-1] ⊕ arr[j] ⊕ arr[j+1] ⊕ … ⊕ arr[k] $$
方法一:暴力
思路
依次枚举i,j,k的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution {public : int countTriplets (vector<int >& arr) { int n = arr.size (); vector<int > pre_arr (n+1 ) ; for (int i = 1 ; i < n+1 ; i++){ pre_arr[i] = pre_arr[i-1 ] ^ arr[i-1 ]; } int a,b,ans=0 ; for (int i=1 ;i<=n;i++) for (int j=i+1 ;j<=n;j++) for (int k=j;k<=n;k++){ a = pre_arr[i-1 ] ^ pre_arr[j-1 ]; b = pre_arr[j-1 ] ^ pre_arr[k]; if (a==b) ans++; } return ans; } };
复杂度分析
时间复杂度:O(n^3),其中 n 是数组 arr 的长度。
空间复杂度:O(n)
方法二:二重循环
思路
由异或的性质可知, $$ a ⊕ b = 0时,arr[i] ⊕ … ⊕ arr[j-1] ⊕ arr[j] ⊕ … ⊕ arr[k] = 0 $$ 因此在 i 之前的前缀异或和到 k 时不会变,即pre_arr[i-1] = pre_arr[k]。
其另一点重点在于在区间 [i, k]内 j在哪并不重要, 因为无论 j在哪,i到k的异或值都等于 0,不影响结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution {public : int countTriplets (vector<int >& arr) { int n = arr.size (), ans = 0 ; vector<int > pre_arr (n+1 ) ; for (int i=1 ;i<n+1 ;i++) pre_arr[i] = pre_arr[i-1 ] ^ arr[i-1 ]; for (int i=1 ;i<n;i++) for (int k=i+1 ;k<=n;k++){ if ((pre_arr[i-1 ] ^ pre_arr[k]) == 0 ) ans += k-i; } return ans; } };
复杂度分析
时间复杂度:O(n^2)
空间复杂度:O(n)
2021-05-19,一看就会,一写就错是我了。
思路 (来自官方题解)
我们用 ⊕ 表示按位异或运算。
由于「按位异或运算」与「加法运算」有着十分相似的性质,它们都满足交换律: $$ a⊕b=b⊕a $$ 以及结合律: $$ (a⊕b)⊕c=a⊕(b⊕c) $$ 因此我们可以使用「前缀和」这一技巧对按位异或运算的结果进行维护。由于本题中给定的矩阵 matrix 是二维的,因此我们需要使用二维前缀和。
设二维前缀和 pre(i,j) 表示矩阵 matrix 中所有满足 0≤x<i 且 0≤y<j 的元素执行按位异或运算的结果 。与一维前缀和类似,要想快速得到 pre(i,j),我们需要已经知道 pre(i−1,j),pre(i,j−1) 以及pre(i−1,j−1) 的结果,即:
$$ pre[i][j] = pre[i-1][j]⊕pre[i][j-1]⊕pre[i-1][j-1]⊕matrix[i-1][j-1]; $$ 当我们将 pre(i−1,j) 和 pre(i,j−1) 进行按位异或运算后,由于对一个数 x 异或两次 y,结果仍然为 x 本身,即: $$ x⊕y⊕y=x $$ 因此 pre(i−1,j−1) 对应区域的按位异或结果被抵消 ,我们需要将其补上,并对位置 (i,j) 的元素进行按位异或运算,这样就得到了 pre(i,j)。
在得到了所有的二维前缀和之后,我们只需要找出其中第 k 大的元素即为答案。这一步我们可以直接将 mn 个二维前缀和进行排序后返第 k 大的元素。
细节
在二维前缀和的计算过程中,如果我们正在计算首行或者首列,即 i=0 或 j=0,此时例如 pre(i−1,j−1) 是一个超出下标范围的结果。因此我们可以使用一个 (m+1)×(n+1) 的二维矩阵,将首行和首列空出来赋予默认值 0,并使用接下来的 m 行和 n 列存储二维前缀和,这样就不必进行下标范围的判断了。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int kthLargestValue (vector<vector<int >>& matrix, int k) { int m = matrix.size (), n = matrix[0 ].size (); vector<vector<int >> pre (m+1 , vector <int >(n+1 )); vector<int > results; for (int i=1 ;i<=m;i++) for (int j=1 ;j<=n;j++){ pre[i][j] = pre[i-1 ][j]^pre[i][j-1 ]^pre[i-1 ][j-1 ]^matrix[i-1 ][j-1 ]; results.push_back (pre[i][j]); } sort (results.begin (),results.end (),greater <int >()); return results[k-1 ]; } };
复杂度分析
2021-5-20,有人相爱,有人夜里开车看海,有人520刷LeetCode (:
方法一:哈希表 + 排序 思路及算法
我们可以预处理出每一个单词出现的频率,然后依据每个单词出现的频率降序排序,最后返回前 k 个字符串即可。
具体地,我们利用哈希表记录每一个字符串出现的频率,然后将哈希表中所有字符串进行排序,排序时,如果两个字符串出现频率相同,那么我们让两字符串中字典序较小的排在前面,否则我们让出现频率较高的排在前面。最后我们只需要保留序列中的前 k 个字符串即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution {public : vector<string> topKFrequent (vector<string>& words, int k) { unordered_map<string, int > map; vector<string> rec; for (auto & word:words) map[word]++; for (auto & [key, value] : map){ rec.emplace_back (key); } sort (rec.begin (),rec.end (), [&](string& a, string& b){ if (map[a]==map[b]) return a < b; else return map[a] > map[b]; }); rec.erase (rec.begin ()+k,rec.end ()); return rec; } };
复杂度分析
时间复杂度:O(l×n+l×mlogm),其中 n 表示给定字符串序列的长度,l 表示字符串的平均长度,m 表示实际字符串种类数。我们需要 l×n 的时间将字符串插入到哈希表中,以及 l × mlogm 的时间完成字符串比较(最坏情况下所有字符串出现频率都相同,我们需要将它们两两比较)。
空间复杂度:O(l×m),其中 l 表示字符串的平均长度,m 表示实际字符串种类数。哈希表和生成的排序数组空间占用均为 O(l×m)。
2021-5-21,对于今天来说,也许这题很奇怪(不相交
思路:动态规划
定义dp[i][j]表示数组nums1的前i个元素和nums2的前j个元素所能绘制的最大连接数,如果要求dp[i][j],我们首先判断nums1的第i个元素和nums2的第j个元素是否相等
如果相等,说明nums1的第i个元素可以和nums2的第j个元素可以连成一条线,这个时候nums1的前i个元素和nums2的前j个元素所能绘制的最大连接数就是nums1的前i-1个元素和nums2的前j-1个元素所能绘制的最大连接数加1,也就是dp[i][j]=dp[i-1][j-1]+1;
如果不相等,我们就把nums1去掉一个元素,计算nums1的前i-1个元素和nums2的前j个元素能绘制的最大连接数,也就是dp[i-1][j],或者把nums2去掉一个元素,计算nums2的前j-1个元素和nums1的前i个元素能绘制的最大连接数,也就是dp[i][j-1],这两种情况我们取最大的即可,所以我们可以找出递推公式:
1 2 3 4 if (nums[i] == nums[j]) dp[i][j] = dp[i-1 ][j-1 ] + 1 ; else dp[i][j] = max (dp[i - 1 ][j], dp[i][j - 1 ]);
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int maxUncrossedLines (vector<int >& nums1, vector<int >& nums2) { int m = nums1. size (), n = nums2. size (); vector<vector<int >> dp (m + 1 , vector <int >(n + 1 )); for (int i = 1 ; i <= m; ++i) for (int j = 1 ; j <= n; ++j) if (nums1[i - 1 ] == nums2[j - 1 ]) dp[i][j] = dp[i - 1 ][j - 1 ] + 1 ; else dp[i][j] = max (dp[i][j - 1 ], dp[i - 1 ][j]); return dp[m][n]; } };
复杂度分析
2021-5-22,一会儿是假新闻,一会儿又是证实,不是很懂央视的操作,袁老一路走好!
思路
要什么思路,我可不会!
代码
你问我代码哪来的?当然是CV啦~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution {public : bool xorGame (vector<int >& nums) { if (nums.size () % 2 == 0 ) { return true ; } int xorsum = 0 ; for (int num : nums) { xorsum ^= num; } return xorsum == 0 ; } }; 作者:LeetCode-Solution 链接:https: 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
复杂度分析
2021-5-23,一看困难,又是异或,那我走?
代码
CV啦~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class Trie {public : const int L = 30 ; Trie* children[2 ] = {}; int min = INT_MAX; void insert (int val) { Trie* node = this ; node->min = std::min (node->min, val); for (int i = L - 1 ; i >= 0 ; --i) { int bit = (val >> i) & 1 ; if (node->children[bit] == nullptr ) { node->children[bit] = new Trie (); } node = node->children[bit]; node->min = std::min (node->min, val); } } int getMaxXorWithLimit (int val, int limit) { Trie* node = this ; if (node->min > limit) { return -1 ; } int ans = 0 ; for (int i = L - 1 ; i >= 0 ; --i) { int bit = (val >> i) & 1 ; if (node->children[bit ^ 1 ] != nullptr && node->children[bit ^ 1 ]->min <= limit) { ans |= 1 << i; bit ^= 1 ; } node = node->children[bit]; } return ans; } }; class Solution {public : vector<int > maximizeXor (vector<int > &nums, vector<vector<int >> &queries) { Trie* t = new Trie (); for (int val : nums) { t->insert (val); } int numQ = queries.size (); vector<int > ans (numQ) ; for (int i = 0 ; i < numQ; ++i) { ans[i] = t->getMaxXorWithLimit (queries[i][0 ], queries[i][1 ]); } return ans; } };
2021-05-26,连着CV了好几天,我是废物,我是废物,我是废物。今天终于可以重拳出击了嘿嘿~
方法一:栈 思路及算法
对于括号序列相关的题目,通用的解法是使用递归或栈。本题中我们将使用栈解决。
我们从左到右遍历该字符串,使用字符串 str 记录当前层所遍历到的小写英文字母。对于当前遍历的字符:
如果是左括号,将 str 插入到栈中,并将 str 置为空,进入下一层;
如果是右括号,则说明遍历完了当前层,需要将 str 反转,返回给上一层。具体地,将栈顶字符串弹出,然后将反转后的 str 拼接到栈顶字符串末尾,将结果赋值给 str。
如果是小写英文字母,将其加到 str 末尾。
注意到我们仅在遇到右括号时才进行字符串处理,这样可以保证我们是按照从括号内到外的顺序处理字符串。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution {public : string reverseParentheses (string s) { stack<string> stk; string str; for (auto & ch:s){ if (ch=='(' ){ stk.push (str); str = "" ; } else if (ch == ')' ){ reverse (str.begin (),str.end ()); str = stk.top () + str; stk.pop (); } else { str.push_back (ch); } } return str; } };
复杂度分析
2021-05-27,一觉醒来十点半,乍看是道简单题,我直接重拳出击。
方法一:位计数
C++(以及其他语言)中内置了计算二进制表达中 1 的数量的函数。在工程中,我们应该直接使用内置函数,但这里我们暂不使用。
__builtin_popcount(x ^ y);
代码
1 2 3 4 5 6 7 8 9 10 11 12 class Solution {public : int hammingDistance (int x, int y) { int t,res=0 ; t = x^y; while (t){ if (t%2 ==1 ) res++; t/=2 ; } return res; } };
复杂度分析
方法二:Brian Kernighan 算法
代码
跳过0直接计算1的个数。
1 2 3 4 5 6 7 8 9 10 11 class Solution {public : int hammingDistance (int x, int y) { int s = x ^ y, ret = 0 ; while (s) { s &= s - 1 ; ret++; } return ret; } };
复杂度分析
2021-05-28,打卡第15天,每天都要坚持呀!
首先想到的是暴力,提交就发现超时了,只能用其他方法了。
方法一:按位统计
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution {public : int totalHammingDistance (vector<int >& nums) { int total=0 , size = nums.size (); int one,zero; for (int i=0 ;i<30 ;i++){ one = 0 ; for (int &num : nums ){ one += (num>>i) & 1 ; } zero = size-one; total += zero * one; } return total; } };
复杂度分析 :
时间复杂度:O(n⋅L)。其中 n 是数组 nums 的长度,L=30。
空间复杂度:O(1)。
2021-5-30,美好的一天从早起开始。
思路
一个数 n 是 2 的幂,当且仅当 n 是正整数,并且 n 的二进制表示中仅包含 1 个 1。
代码一
1 2 3 4 5 6 class Solution {public : bool isPowerOfTwo (int n) { return n > && ( n & (n - 1 )) == 0 ; } };
代码二
1 2 3 4 5 6 7 class Solution {public : bool isPowerOfTwo (int n) { return n > 0 && (n & -n) == n; } };
复杂度分析:
时间复杂度:O(1)
空间复杂度:O(1)
2021-05-31,乒乓球考试终于考完啦~
方法一:二进制表示中 1 的位置
思路
先判断是不是2的倍数,再判断是不是4的倍数。
代码
1 2 3 4 5 6 7 class Solution {public : bool isPowerOfFour (int n) { int x = 0b101010101010101010101010101010101 ; return n>0 && (n&(n-1 ))==0 && (x&n)!=0 ; } };
复杂度分析
时间复杂度:O(1)
空间复杂度:O(1)
方法二:取模性质
思路
4的幂有两条性质:
二进制中只有一个1
除以3余1
代码
1 2 3 4 5 6 class Solution {public : bool isPowerOfFour (int n) { return n>0 && (n&(n-1 ))==0 && ((n%3 )==1 ); } };
复杂度分析
时间复杂度:O(1)
空间复杂度:O(1)
2021-06-02,昨天遇见一位说话很可爱(欠)的女生(聊天中发现,可爱这个词她觉得并不准确,可我觉得没问题呀,但我还是换成了她对自己的描述:欠 ○( ^皿^)っHiahiahia…),幸好要到了联系方式,否则我真的会挂念很久的。一开始也是怂了,其实只要敢迈出这一步真的不难…
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # 前缀 class Solution {public : bool checkSubarraySum (vector<int >& nums, int k) { int m = nums.size (); if (m < 2 ) { return false ; } unordered_map<int , int > mp; mp[0 ] = -1 ; int remainder = 0 ; for (int i = 0 ; i < m; i++) { remainder = (remainder + nums[i]) % k; if (mp.count (remainder)) { int prevIndex = mp[remainder]; if (i - prevIndex >= 2 ) { return true ; } } else { mp[remainder] = i; } } return false ; } };
复杂度分析
时间复杂度:O(m),其中 m 是数组 nums 的长度。需要遍历数组一次。
空间复杂度:O(min(m,k)),其中 m 是数组 nums 的长度。空间复杂度主要取决于哈希表,哈希表中存储每个余数第一次出现的下标,最多有 min(m,k) 个余数。
2021-06-04,走到尽头见不到你,于是走过你来时的路,等到相遇时才发现,你也走过我来时的路。
思路
利用集合,先遍历一遍链表A,将A中的所节点存入set,再遍历链表B,如果遇到当B中的某一节点出现在set中,说明该节点是相交节点,返回该节点即可,没找到返回null。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Solution {public : ListNode *getIntersectionNode (ListNode *headA, ListNode *headB) { unordered_set<ListNode *> Set; ListNode *p = headA; while (p){ Set.insert (p); p=p->next; } p=headB; while (p){ if (Set.find (p)!=Set.end ()) return p; p=p->next; } return nullptr ; } };
复杂度分析
时间复杂度:O(m+n),其中 m 和 n 是分别是链表 headA 和 headB 的长度。两个指针同时遍历两个链表,每个指针遍历两个链表各一次。
空间复杂度:O(1)。
思路
递归yyds
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution {public : ListNode* removeElements (ListNode* head, int val) { if (head==nullptr ) return head; head->next = removeElements (head->next, val); if (head->val == val) return head->next; else return head; } };
复杂度分析
2021-07-05,一个月没更新了,今天终于想起来了,一看题,哦困难啊,告辞告辞。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 class Solution {public : string countOfAtoms (string formula) { int i = 0 , n = formula.length (); auto parseAtom = [&]() -> string { string atom; atom += formula[i++]; while (i < n && islower (formula[i])) { atom += formula[i++]; } return atom; }; auto parseNum = [&]() -> int { if (i == n || !isdigit (formula[i])) { return 1 ; } int num = 0 ; while (i < n && isdigit (formula[i])) { num = num * 10 + int (formula[i++] - '0' ); } return num; }; stack<unordered_map<string, int >> stk; stk.push ({}); while (i < n) { char ch = formula[i]; if (ch == '(' ) { i++; stk.push ({}); } else if (ch == ')' ) { i++; int num = parseNum (); auto atomNum = stk.top (); stk.pop (); for (auto &[atom, v] : atomNum) { stk.top ()[atom] += v * num; } } else { string atom = parseAtom (); int num = parseNum (); stk.top ()[atom] += num; } } auto &atomNum = stk.top (); vector<pair<string, int >> pairs; for (auto &[atom, v] : atomNum) { pairs.emplace_back (atom, v); } sort (pairs.begin (), pairs.end ()); string ans; for (auto &p : pairs) { ans += p.first; if (p.second > 1 ) { ans += to_string (p.second); } } return ans; } };