1. 两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
给定 nums = [2, 7, 11, 15], target = 9 因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]
解题思路-1:
存入map中,查找差值是否也在数组中,是则返回。
注意:一个元素只能用一次,数组可以有重复元素。
1 class Solution { 2 public: 3 vector<int> twoSum(vector<int>& nums, int target) { 4 int sz = nums.size(); 5 vector<int> res; 6 if(sz<2) 7 return res; 8 9 map<int,int> m; 10 for(int i=0;i<sz;i++){ 11 m[nums[i]]=i; 12 } 13 14 for(int i=0;i<sz-1;i++){ 15 if(m.find(target-nums[i]) != m.end() && m[target-nums[i]]!=i){ 16 res.push_back(i); 17 res.push_back(m[target-nums[i]]); 18 break; //不要忘记加break,否则会重复计算。 19 } 20 } 21 return res; 22 } 23 }; 24 /* 25 遇到的错误: 26 没有break:如果返回元素是索引 1与4,在1处添加1与4;在4处还会重复添加4与1。 27 if的判断条件有&& 28 后一个判断条件是索引不相等,而不是值不相等。因为数组中可能出现相同的元素。如[3,3] 29 */
解题思路-2:
使用一个HashMap(即 unordered_map)
1 class Solution { 2 public: 3 vector<int> twoSum(vector<int>& nums, int target) { 4 unordered_map<int, int> m; 5 vector<int> res; 6 for (int i = 0; i < nums.size(); ++i) { 7 m[nums[i]] = i; 8 } 9 for (int i = 0; i < nums.size(); ++i) { 10 int t = target - nums[i]; 11 if (m.count(t) && m[t] != i) { 12 res.push_back(i); 13 res.push_back(m[t]); 14 break; 15 } 16 } 17 return res; 18 } 19 };
简洁版(推荐,一次for循环即可)
1 class Solution { 2 public: 3 vector<int> twoSum(vector<int>& nums, int target) { 4 unordered_map<int, int> m; 5 for (int i = 0; i < nums.size(); ++i) { 6 if (m.count(target - nums[i])) { //当前i还没有被存入,找到的是之前存入的,因此不会出现找到的两个数是同一个元素的情况 7 return {i, m[target - nums[i]]}; 8 } 9 m[nums[i]] = i; 10 } 11 return {}; 12 } 13 };
2. 两数相加
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 输出:7 -> 0 -> 8 原因:342 + 465 = 807
解题思路:
从两个链表的头部开始计算,如果两个链表的值以及进位相加>=10,则继续进位(设置标志flag表示是否需要进位)
注意:
1)当两个链表共有长度处理完,还要处理较长的链表的剩余部分,并不是简单链接,还要考虑是否有共同相加时的进位。
2)当长的链表处理完,要考虑最高位(即最后一位是否有进位),如果有进位,添加一个新节点。
1 /** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode(int x) : val(x), next(NULL) {} 7 * }; 8 */ 9 class Solution { 10 public: 11 ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { 12 if(l1==nullptr && l2==nullptr ) 13 return nullptr; 14 ListNode* res=new ListNode(0); 15 ListNode* p1=l1; 16 ListNode* p2=l2; 17 ListNode* phead = res; 18 int flag = 0,temp=0; 19 while(p1!=nullptr && p2!=nullptr){ //处理两个链表的公共部分 20 temp = p1->val + p2->val + flag; 21 if(temp>=10){ 22 flag =1; 23 temp = temp%10; 24 }else{ 25 flag = 0; 26 } 27 ListNode* tp = new ListNode(temp); 28 phead->next = tp; 29 phead = tp; 30 p1=p1->next; 31 p2=p2->next; 32 } 33 ListNode* pp=nullptr; 34 if(p1 != nullptr) 35 pp=p1; 36 if(p2 != nullptr) 37 pp=p2; 38 //处理长的某个非空链表的剩余部分(注意进位问题) 39 while(pp!=nullptr){ 40 temp = pp->val + flag; 41 if(temp>=10){ 42 flag =1; 43 temp = temp%10; 44 }else{ 45 flag = 0; 46 } 47 ListNode* tp = new ListNode(temp); 48 phead->next = tp; 49 phead = tp; 50 pp=pp->next; 51 } 52 //最高位是否需要进位 53 if(flag){ 54 ListNode* tp = new ListNode(flag); 55 phead->next = tp; 56 } 57 //注意链表的返回 58 return res->next; 59 } 60 };
注意:
定义新的链表的时候:是ListNode* res=new ListNode(0); 而不是ListNode* res=nullptr; (后一种情况,空指针,没有办法访问,res->val或者res->next)
前一种定义是正确的,返回是应该返回 res->next而不是res;
简洁版:
1 public ListNode addTwoNumbers2(ListNode l1, ListNode l2) { 2 ListNode dummyHead = new ListNode(0); 3 ListNode p = l1, q = l2, curr = dummyHead; 4 int carry = 0; 5 while (p != null || q != null) { 6 int x = (p != null) ? p.val : 0; 7 int y = (q != null) ? q.val : 0; 8 int sum = carry + x + y; 9 carry = sum / 10; 10 curr.next = new ListNode(sum % 10); 11 curr = curr.next; 12 if (p != null) p = p.next; 13 if (q != null) q = q.next; 14 } 15 if (carry > 0) { 16 curr.next = new ListNode(carry); 17 } 18 return dummyHead.next; 19 }
3. 无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
解题思路-1:
使用队列、集合。集合用于判断是否出现重复元素。队列用于保存出现重复元素时,当前不重复的子串(将队首重复的元素,及之前的元素删掉)。
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 int sz = s.size(); 5 if(sz<2) 6 return sz; 7 8 set<char> setS; 9 queue<char> qS; 10 setS.insert(s[0]); 11 qS.push(s[0]); 12 int count = 1,temp=1; 13 for(int i=1;i<sz;i++){ //从索引1开始 14 auto iter = setS.find(s[i]); 15 if(iter!=setS.end()){ //找到元素,即出现相同元素 16 auto itQ = qS.front(); 17 while(itQ != s[i]){ 18 qS.pop(); 19 setS.erase(itQ); 20 temp--; 21 itQ = qS.front();//不要忘记更新变量,指向下一个元素 22 } 23 qS.pop(); // a b c a 集合与队列中已经有3个元素,对于集合,新来了a,其元素由啊a b c变为b c a。可以保持不变 24 qS.push(s[i]); //但对于队列,其设计顺序问题,若a b c保持不变(实际上是b c a),下一次进入时,a b c 会将a弹掉(错误) 25 }else{ //没有重复元素 26 qS.push(s[i]); 27 setS.insert(s[i]); 28 temp++; 29 } 30 count = max(count,temp); 31 } 32 return count; 33 } 34 }; 35 36 //队列没有迭代器,qS.begin()是错误的
注意:1)队列(queue)没有迭代器 2)当队列中元素是 a b c时,进入元素a,应该将对首a删除,然后队尾添加a,保持相邻元素的顺序。
解题思路-2:
上述思路使用队列消耗了多余的空间。实际上使用两个指针来表示当前无重复字符的子串即可以
1 class Solution { 2 public: 3 int lengthOfLongestSubstring(string s) { 4 int sz = s.size(); 5 if(sz<2) 6 return sz; 7 8 int start=0,end=1; 9 int count = end - start; 10 unordered_set<char> setS; 11 setS.insert(s[0]); 12 13 for(int i=1;i<sz;i++){ 14 if(setS.find(s[i])!=setS.end()){//找到重复元素 15 while(s[start]!=s[i]){ 16 setS.erase(s[start]); //不要忘记删除队首多余的元素 17 start++; 18 } 19 start++; //两者同时后移,因为while只处理了重复元素的前面的元素,而没有考虑之后的元素 20 end++; 21 }else{ 22 end++; 23 setS.insert(s[i]); 24 } 25 count = max(count,end-start); 26 27 } 28 return count; 29 } 30 };