LeetCode精选好题(三)

举报
看,未来 发表于 2020/12/29 22:51:29 2020/12/29
【摘要】 选进来的,都是我二刷之后确定我自己会做的。 文章目录 1、矩阵置零思路:代码实现: 2、字母异位词分组思路:代码实现: 3、无重复字符的最长子串思路:代码实现: 4、两数相加代码实现: 5、奇偶链表思路代码实现: 6、相交链表思路:代码实现: 1、矩阵置零 给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素...

选进来的,都是我二刷之后确定我自己会做的。

1、矩阵置零

给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。

示例 1:

输入: 
[
  [1,1,1],
  [1,0,1],
  [1,1,1]
]

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
输出: 
[
  [1,0,1],
  [0,0,0],
  [1,0,1]
]

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

示例 2:

输入: 
[
  [0,1,2,0],
  [3,4,5,2],
  [1,3,1,5]
]

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
输出: 
[
  [0,0,0,0],
  [0,4,5,0],
  [0,3,1,0]
]

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

进阶:

一个直接的解决方案是使用  O(mn) 的额外空间,但这并不是一个好的解决方案。
一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
你能想出一个常数空间的解决方案吗?

   
  
  • 1
  • 2
  • 3

思路:

也不想多说,直接暴力O(1)解法吧。
在遍历过程中,遇到零,就把那一行那一列中不为零的部分设为INT_MIN.
第二遍遍历时把值为INT_MIN的设为0。
不过条件判断和循环嵌套会很繁琐。

代码实现:

class Solution {
void setZeroes(vector<vector<int>>& matrix) { int R = matrix.length; int C = matrix[0].length; for (int r = 0; r < R; r++) { for (int c = 0; c < C; c++) { if (matrix[r][c] == 0) { // We modify the corresponding rows and column elements in place. // Note, we only change the non zeroes to MODIFIED for (int k = 0; k < C; k++) { if (matrix[r][k] != 0) { matrix[r][k] = INT_MIN; } } for (int k = 0; k < R; k++) { if (matrix[k][c] != 0) { matrix[k][c] = INT_MIN; } } } } } for (int r = 0; r < R; r++) { for (int c = 0; c < C; c++) { // Make a second pass and change all INT_MIN elements to 0 """ if (matrix[r][c] == INT_MIN) { matrix[r][c] = 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
  • 30
  • 31
  • 32
  • 33

2、字母异位词分组

给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。

示例:

输入: ["eat", "tea", "tan", "ate", "nat", "bat"]

  
 
  • 1
输出:
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

说明:

所有输入均为小写字母。
不考虑答案输出的顺序。

   
  
  • 1
  • 2

思路:

将字符串进行排序之后作为map的键值,将原字符串作为实值填入map当中,之后遍历map将值取出。
Map<string,vector>

代码实现:

vector<vector<string>> groupAnagrams(vector<string>& strs) { vector<vector<string>> res; map<string,vector<string>> vec; if(strs.empty()) return res; for(int i=0;i<strs.size();i++) { string temp=strs[i]; sort(temp.begin(),temp.end()); vec[temp].push_back(strs[i]); } map<string,vector<string>>::iterator it; for(auto it=vec.begin();it!=vec.end();it++) { res.push_back(it->second); } return res; }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

3、无重复字符的最长子串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: "abcabcbb"

  
 
  • 1
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3
 
  • 1
  • 2

示例 2:

输入: "bbbbb"

  
 
  • 1
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1
 
  • 1
  • 2

示例 3:

输入: "pwwkew"

  
 
  • 1
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

  
 
  • 1
  • 2
  • 3

思路:

这道题主要用到思路是:滑动窗口
什么是滑动窗口?
其实就是一个队列,比如例题中的 abcabcbb,进入这个队列(窗口)为 abc 满足题目要求,当再进入 a,队列变成了 abca,这时候不满足要求。所以,我们要移动这个队列!

如何移动?
我们只要把队列的左边的元素移出就行了,直到满足题目要求!
一直维持这样的队列,找出队列出现最长的长度时候,求出解!
时间复杂度:O(n)

代码实现:

class Solution {
public: int lengthOfLongestSubstring(string s) { if(s.size() == 0) return 0; unordered_set<char> lookup; int maxStr = 0; int left = 0; for(int i = 0; i < s.size(); i++){ while (lookup.find(s[i]) != lookup.end()){ lookup.erase(s[left]); left ++; } maxStr = max(maxStr,i-left+1); lookup.insert(s[i]); } return maxStr; }
};

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

4、两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

  
 
  • 1
  • 2
  • 3

代码实现:

ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { ListNode *dummyHead = new ListNode(0); ListNode *p = l1, *q = l2, *curr = dummyHead; int carry = 0; while (p != NULL || q != NULL) { int x = (p != NULL) ? p->val : 0; int y = (q != NULL) ? q->val : 0; int sum = carry + x + y; carry = sum / 10; curr->next = new ListNode(sum % 10); curr = curr->next; if (p != NULL) p = p->next; if (q != NULL) q = q->next; } if (carry > 0) { curr->next = new ListNode(carry); } return dummyHead->next; }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

提高篇:如果链表是顺序存储的呢?
我的想法:原地逆置。

5、奇偶链表

给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

示例 1:

输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL

  
 
  • 1
  • 2

示例 2:

输入: 2->1->3->5->6->4->7->NULL 
输出: 2->3->6->7->1->5->4->NULL

  
 
  • 1
  • 2

说明:

应当保持奇数节点和偶数节点的相对顺序。
链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。

   
  
  • 1
  • 2

思路

快慢指针,对点交换,没有压力。
哦,不,不是交换,我中了交换算法的毒。应该把快指针的节点直接提到前边去就好。

代码实现:

ListNode* oddEvenList(ListNode* head) { if (head == NULL) return head; ListNode* fast = head;  //快 ListNode* slow = head;  //慢 ListNode* temp = head;  //临时 int flag = 1; while (1) { for (int i = flag; i > 0 && fast->next != NULL; i--) { fast = fast->next; } if (fast->next != NULL) { temp = fast->next; fast->next = temp->next; temp->next = slow->next; slow->next = temp; fast = temp; slow = temp; flag++; } else { break; } } return head;
}

  
 
  • 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

6、相交链表

编写一个程序,找到两个单链表相交的起始节点。
如下面的两个链表:
在这里插入图片描述
在节点 c1 开始相交。

示例 1:
在这里插入图片描述

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8

  
 
  • 1
  • 2

输入解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:
在这里插入图片描述

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2

  
 
  • 1
  • 2

输入解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:
在这里插入图片描述

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null

  
 
  • 1
  • 2

输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。

注意:

如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

   
  
  • 1
  • 2
  • 3
  • 4

思路:

看这题目哔哔一堆,翻转过来不就结了嘛。
然后,就出问题了。。。
在“注意”里面,还安排了这一条小字:在返回结果后,两个链表仍须保持原有的结构

草率了。。。

所以我们换一个解法吧,正面应战。
1、先把两条链表到底有多长摸清楚。
2、两条链表从等长的地方开始匹配。

然后,我们再看一下图和题目:是相交链表,就是说,后半部分其实是共用地址的,前面俩解法一直把这两条链表当成了值相同的两条不相干的链表。所以,就有另一种说法了,想一想链表成环的时候寻找入环点怎么找。

又草率了。。。

所以我们再换一种更简单的解法吧:
一种比较巧妙的方式是,分别为链表A和链表B设置指针A和指针B,然后开始遍历链表,如果遍历完当前链表,则将指针指向另外一个链表的头部继续遍历,直至两个指针相遇。
最终两个指针分别走过的路径为:
指针A :a+c+b
指针B :b+c+a
明显 a+c+b = b+c+a,因而如果两个链表相交,则指针A和指针B必定在相交结点相遇。

在这里插入图片描述

代码实现:

 ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { ListNode *a = headA, *b = headB; while(a != b) { a = a? a->next : headB; //为NULL时,将值置为另一个链表的起点 b = b? b->next : headA; } return a; }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

文章来源: lion-wu.blog.csdn.net,作者:看,未来,版权归原作者所有,如需转载,请联系作者。

原文链接:lion-wu.blog.csdn.net/article/details/107670727

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。