第二周,
Alogrithm 题目—Add Two Numbers You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
Example:
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807.
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 class Solution {public : ListNode* addTwoNumbers (ListNode* l1, ListNode* l2) { unsigned int pre_sum = 0 ; ListNode *prev_node = nullptr , *output = nullptr ; while (l1 != nullptr || l2 != nullptr ) { unsigned int a = 0 , b = 0 , sum = 0 ; if (l1 != nullptr ) { a = l1->val; l1 = l1->next; } if (l2 != nullptr ) { b = l2->val; l2 = l2->next; } pre_sum = adder (a, b, pre_sum, &sum); auto node = new ListNode (sum); if (prev_node == nullptr ) { output = node; } else { prev_node->next = node; } prev_node = node; } if (pre_sum != 0 ) { prev_node->next = new ListNode (pre_sum); } return output; } private : inline int adder (const unsigned int &a, const unsigned int &b, const unsigned int &pre_sum, unsigned int *res) { int sum = a + b + pre_sum; *res = sum % 10 ; return sum / 10 ; } };
68ms,还是慢,需要想想怎么优化。
在网上找到的duolabaobao的答案
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 :ListNode* addTwoNumbers (ListNode* l1, ListNode* l2) { ListNode *preHead = new ListNode (0 ); ListNode *p = preHead; int carry = 0 , sum; while (l1 || l2 || carry) { sum = (l1 ? l1->val : 0 ) + (l2 ? l2->val : 0 ) + carry; carry = sum / 10 ; p->next = new ListNode (sum % 10 ); p = p->next; l1 = l1 ? l1->next : NULL ; l2 = l2 ? l2->next : NULL ; } return preHead->next; }
简洁了特别多,主要在于如下几点:
不用傻乎乎写if (l1 != nullptr)而直接判断if (l1)就好
赋值判断可以直接通过<condition>?<true>:<false>实现
传上去发现也得64ms
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 class Solution {public : ListNode* addTwoNumbers (ListNode* l1, ListNode* l2) { ListNode* root = new ListNode (0 ), *first = root; int carry = 0 ; while ( l1 || l2 ) { int n1 = l1 ? l1->val : 0 ; int n2 = l2 ? l2->val : 0 ; int ans = n1 + n2 + carry; carry = ans / 10 ; root->next = new ListNode (ans%10 ); root = root->next; if ( l1 ) l1 = l1->next; if ( l2 ) l2 = l2->next; } if (carry) root->next = new ListNode (1 ); return first->next; } };
前面的问题在于while的判断次数比较多,需要把carray单独拆出来不用那么复杂的判断再跑一遍。同时不能有太多判断什么的操作放在里面,初始化什么的尽量在外面做好。
题目 - Longest Substring Without Repeating Characters 查找没有重复字母的最大子串。
最Brute Force的方法略去,想到可以做一个string记录目前检验的子串,用一个int存储当前最大长度的子串长度。用一个循环遍历每个字母,如果这个字母在buf子串里出现过,就更新buf子串去掉前面的部分并对比目前最大子串长度值看是否需要更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int Solution::lengthOfLongestSubstring (std::string s) { std::string buf = "" ; int max = 0 ; for (auto i : s) { auto res = buf.find (i); if (res == -1 ) { buf += i; } else { if (buf.length () > max) max = buf.length (); buf = buf.substr (res + 1 ) + i; } } if (buf.length () > max) max = buf.length (); return max; }
由于std::string::find的复杂度为O(N * M),在本例中M为1,所以最差时间复杂度为O(N^2),好处是后面一个N不会大于128(ASCII),所以其实可以说是O(128N),但还可以再优化。
优化的方向是放弃用一个字符串来存储找到的字段,直接用HashMap存储字母-位置的对应关系,用一个int记录目前不重复字符串的头位置。如果遇到重复只用更新头位置和对应关系里的位置,并更新最大长度就好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int Solution::lengthOfLongestSubstring (std::string s) { std::unordered_map<char , int > map; int max = 0 , start = 0 ; for (int i = 0 ; i != s.length (); i++) { auto found = map.find (s[i]); if (found != map.end () && found->second >= start) { max = (max > (i - start)) ? max : (i - start); start = found->second + 1 ; } map[s[i]] = i; } max = (max > (s.length () - start)) ? max : (s.length () - start); return max; }
Review 本周文章是一个2016年的slide : gRPC Design and Implementation :Review - gRPC Design and Implementation
Tip 安装过程用有很多时候需要知道电脑或服务器的架构,比如说到底是:
x86
x86-64/amd64
MIPS
ARM 其中的那种。
这个问题可以通过一行命令:
解决。或者可以使用:
1 2 3 lscpu less /proc/cpuinfo
了解
Share 本周推荐Youtube上一个节目频道:Vox Borders
讲各种有趣的边境故事的。