LeetCode数组
26. Remove Duplicates from Sorted Array
Remove Duplicates from Sorted Array
从小到大的有序数组,要求在$O(1)$空间内,去除重复元素
80. Remove Duplicates from Sorted Array II
Remove Duplicates from Sorted Array II
从小到大的有序数组,要求在$O(1)$空间内,去除重复元素,每个元素最多出现2次。
以上两道题类似的结构,解法也一样,用两个指针即可。
1
2
3
4
5
6
7
|
public int removeDuplicates(int[] nums) {
int i = 0;
for (int n : nums)
if (i < 2 || n > nums[i-2])
nums[i++] = n;
return i;
}
|
83. Remove Duplicates from Sorted List「两个指针」
Remove Duplicates from Sorted List
给定一个有序链表,去除重复元素。
Example 1:
Input: 1->1->2
Output: 1->2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public ListNode deleteDuplicates(ListNode head) {
if(head == null)
return null;
ListNode res = new ListNode(0);
res.next = head;
ListNode cur = head, pre = res;
while (cur != null)
{
while (cur.next != null && cur.val == cur.next.val)
{
cur = cur.next;
}
if(pre.next != cur)
{// no duplicates
pre.next = cur;
}
pre = pre.next;
cur = cur.next;
}
return res.next;
}
|
82. Remove Duplicates from Sorted List II「两个指针」
Remove Duplicates from Sorted List II
给定一个有序链表,去除重复元素,要求不保留。
Example:
Input: 1->1->1->2->3
Output: 2->3
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
|
public ListNode deleteDuplicates(ListNode head) {
if(head == null)
return head;
ListNode res = new ListNode(0);
res.next = head;
ListNode pre = res, cur = res.next;
while (cur!=null)
{
while (cur.next!=null && cur.val == cur.next.val)
{
cur = cur.next;
}
if (pre.next == cur)
{
pre = pre.next;
cur = cur.next;
}
else
{ // duplicate detected
pre.next = cur.next;
cur = pre.next;
}
}
return res.next;
}
|
86. Partition List「两个指针」
Partition List
给定一个链表,和一个值x。要求完成一次快排的partition操作。
直接创立两个链表,一个保存小于x
的结点,一个保存大于等于x
的结点,最后将两个链表连接即可。
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
|
public static class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
}
}
public ListNode partition(ListNode head, int x) {
ListNode p = new ListNode(0), q = new ListNode(0);
ListNode L1 = p, L2 = q;
while (head!=null)
{
if(head.val < x)
{
L1.next = head;
L1 = L1.next;
}else {
L2.next = head;
L2 = L2.next;
}
head = head.next;
}
L1.next = q.next;
L2.next = null;
return p.next;
}
|
88. Merge Sorted Array「两个指针」
Merge Sorted Array
给定两个有序数组nums1和nums2,其中nums1空间足够大,要求合并到nums1中使得整个数组依旧有序。
采用两个指针,一遍遍历即可。注意从后往前扫描,因为后面是有预留空间的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public void merge(int[] nums1, int m, int[] nums2, int n) {
int end = m + n - 1;
int p = m - 1, q = n - 1;
while (p >= 0 && q >= 0) {
if(nums1[p] < nums2[q]){
nums1[end--] = nums2[q--];
}else{
nums1[end--] = nums1[p--];
}
}
while (q>=0)
{
nums1[end--] = nums2[q--];
}
}
|
92. Reverse Linked List II「四个指针」
Reverse Linked List II
给定一个链表,反转其从m到n的结点。要求一遍遍历。
采取逻辑上比较简单的思路,即把待反转的子链表通过插入新链表的方式反转,再拼接。
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
|
public static class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
}
}
public ListNode reverseBetween(ListNode head, int m, int n) {
ListNode dummy = new ListNode(0), rev_dummy = new ListNode(0); //反转子链表的头结点
ListNode rev_end = null; //指向反转后的最后一个结点
dummy.next = head;
ListNode fast = head, slow = dummy;
for(int index = 1; index<=n; index++)
{
if(index < m)
{
fast = fast.next;
slow = slow.next;
}
else{
if(index == m)
rev_end = fast;
ListNode tmp = fast;
fast = fast.next;
tmp.next = rev_dummy.next;
rev_dummy.next = tmp;
}
}
slow.next = rev_dummy.next;
rev_end.next = fast;
return dummy.next;
}
|
138-Copy List with Random Pointer「链表」
Copy List with Random Pointer
深拷贝链表,关键在于random字段的填充。
采取一个比较简单的思路,第一次遍历用一个map保存原结点-新结点的对,然后第二次遍历填充next和random字段。
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
|
class Node {
public:
int val;
Node* next;
Node* random;
Node() {}
Node(int _val, Node* _next, Node* _random) {
val = _val;
next = _next;
random = _random;
}
};
class Solution {
public:
Node* copyRandomList(Node* head) {
unordered_map<Node*, Node*> mm;
Node* p = head;
while (p)
{
mm[p] = new Node(p->val, nullptr, nullptr);
p = p->next;
}
p = head;
while (p){
mm[p]->next = mm[p->next];
mm[p]->random = mm[p->random];
p = p->next;
}
return mm[head];
}
};
|
ps: map用[]访问不到,会插入元素
141-Linked List Cycle「链表」
Linked List Cycle
判断链表是否有环。
O(1)的方法,设置两个指针,一个走两步,一个走一步,如果有环它们终会回合,否则就是走到空指针循环结束。
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
|
struct ListNode {
int val;
ListNode *next;
explicit ListNode(int x) : val(x), next(nullptr) {}
};
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head == nullptr)
return false;
ListNode* one = head;
ListNode* two = head;
while (two->next && two->next->next)
{
one = one->next;
two = two->next->next;
if(one == two)
return true;
}
return false;
}
};
|
142-Linked List Cycle II[两个指针]
Linked List Cycle II
同上一题,不过需要返回环开始的结点。解法也类似。
设$A$表示起点到环开始结点的距离,$B$为环开始结点到第一次相遇的距离,$N$表示环的长度。则有$2A+2B-(A+B)=A+B=N$。
因此找到第一次相遇的结点后,让慢指针继续走同时头结点再开始一个慢指针,它们相遇的结点即是环的开始。
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:
ListNode *detectCycle(ListNode *head) {
if(head == nullptr)
return nullptr;
ListNode *one = head, *two = head;
while (two && two->next)
{
one = one->next;
two = two->next->next;
if(one == two)
{
ListNode* one2 = head;
while(one != one2)
{
one = one->next;
one2 = one2->next;
}
return one2;
}
}
return nullptr;
}
};
|
148-Sort List[归并排序]
Sort List
对链表进行排序,要求O(nlogn)的时间复杂度且用常量的空间复杂度。
考虑使用迭代版归并排序(不使用递归是因为递归本身需要用栈,不是常量空间)。
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
struct ListNode {
int val;
ListNode *next{};
explicit ListNode(int x) : val(x), next(nullptr) {}
};
class Solution {
private:
ListNode *split(ListNode*head, int n)
{//从head中取前n个结点,返回剩下的链表的首结点指针
for(int i=1; head&&i<n; i++)
head = head->next;
if(!head)
return nullptr;
else
{
ListNode* second = head->next;
head->next = nullptr;
return second;
}
}
ListNode *merge(ListNode*left, ListNode*right, ListNode*tail)
{//把left和right链表按顺序追加到tail尾部,最后返回链表尾部的指针
ListNode * cur = tail;
while (left&&right)
{
if(left->val < right->val )
{
cur->next = left;
cur = left;
left=left->next;
} else{
cur->next = right;
cur = right;
right = right->next;
}
}
while (left)
{
cur->next = left;
cur = left;
left = left->next;
}
while (right)
{
cur->next = right;
cur = right;
right = right->next;
}
return cur;
}
public:
ListNode *sortList(ListNode *head) {
if(!head || !(head->next)) return head;
//先求链表长度
ListNode* p = head;
int length = 0;
while(p)
{
length++;
p=p->next;
}
ListNode dummy(0);//增加一个头结点便于插入
dummy.next = head;
ListNode *left, *right, *tail, *cur;
for(int step = 1; step<length; step*=2)
{
cur = dummy.next;
tail = &dummy;
while(cur)
{
left = cur;
right = split(left, step);
cur = split(right, step);
tail = merge(left, right, tail);
}
}
return dummy.next;
}
};
|
160-Intersection of Two Linked Lists「两个指针」
Intersection of Two Linked Lists
找到两个链表相交的开始阶段,不相交返回null。
和141相似,同样设置两个指针。假设链表1起点到相交结点的距离为A,链表2起点到相交结点距离为B,两个链表重合部分长度为C。
基本想法是:两个指针p、q分别从链表1、2起点开始,每次走一步。如果其中一个到了结尾(为null),则让其从另一个链表的头部开始。
显然,如果两个链表相交,则两个指针第二圈一定会重合,重合的地方就是开始的地方(都走了$A+B+C$的长度)。如果没有相交,最后它们都会走到结尾,为null,因为都走了$A+B+2C$的长度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
struct ListNode {
int val;
ListNode *next;
explicit ListNode(int x) : val(x), next(nullptr) {}
};
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *p=headA, *q=headB;
while (p!=q)
{
p = p?p->next:headB;
q = q?q->next:headA;
}
return p;
}
};
|
PS:nullptr==nullptr
为true。
238-Product of Array Except Self「数组」
Product of Array Except Self
给定一个数组,要求返回一个数组,每个元素等于原数组除对应元素外其它所有元素的乘积。
先用一个数组记录累积,product[i]
记录着从nums[0]
到nums[i-1]
的乘积。然后再从数组右侧开始遍历,用一个元素记录累积,这样就可以把当前元素nums[i]
空出来了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public int[] productExceptSelf(int[] nums) {
int[] product = new int[nums.length];
product[0] = 1;
for(int i = 1; i<nums.length; i++)
{
product[i] = product[i-1] * nums[i-1];
}
int right = 1;
for(int i = nums.length-1; i>=0; i--)
{
product[i] = product[i] * right;
right = right * nums[i];
}
return product;
}
|
581-Shortest Unsorted Continuous Subarray[两个指针]
Shortest Unsorted Continuous Subarray
给定一个数组,找到一个连续子数组,如果这个子数组升序排序,那么整个数组升序有序。
采用两个指针,直接前后开始找子数组的起点和终点这种方式不可行。比如:
1
2
3
|
Input: [2, 6, 4, 8, 10, 9, 15]
Output: 5
Explanation: You need to sort [6, 4, 8, 10, 9] in ascending order to make the whole array sorted in ascending order. |
这里应该从6开始,而不是从4开始。
转换一下思路,从数组最后向前扫描,找到起点;从数组开始向后扫描,找到终点。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public int findUnsortedSubarray(int[] nums) {
int start=-1, end = -1;
int max = nums[0], min = nums[nums.length-1];
for(int i =1; i<nums.length; i++)
{
max = Math.max(nums[i], max); // 数组前部分最大值
min = Math.min(nums[nums.length-1-i], min); //数组后面的而最小值
if(nums[i] < max)
{
end = i;
}
if(nums[nums.length-1-i]>min)
start = nums.length-1-i;
}
if(start==-1&&end==-1)
return 0;
return end - start + 1;
}
|