刷题日记 - 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 26 27 28 29 30 31 32 33 34 35 36
| class Solution { public void merge(int[] nums1, int m, int[] nums2, int n) { int i = 0; int j = 0; int[] copy = new int[m]; System.arraycopy(nums1, 0, copy, 0, m);
int num1; int num2; int idx = 0; while (i < m && j < n) { num1 = copy[i]; num2 = nums2[j]; if (num1 <= num2) { nums1[idx] = num1; i++; } else { nums1[idx] = num2; j++; } idx++; } while (i < m) { nums1[idx++] = copy[i++]; } while (j < n) { nums1[idx++] = nums2[j++]; } } }
|
先将nums1
数组复制到额外空间中,便于访问。
然后利用两个指针和一个idx
,和合并有序链表的思想类似。
难度:简单
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
|
class Solution { public ListNode mergeTwoLists(ListNode list1, ListNode list2) { ListNode dummy = new ListNode(-1); ListNode p = list1; ListNode q = list2; ListNode t = dummy;
while (p != null && q != null) { if (p.val <= q.val) { t.next = p; t = t.next; p = p.next; } else { t.next = q; t = t.next; q = q.next; } }
t.next = (p == null) ? q : p;
return dummy.next; } }
|
难度:困难
可以复用上个题的代码,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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
|
class Solution { public ListNode mergeKLists(ListNode[] lists) { if (lists.length == 0) return null; for (int i = 0; i < lists.length - 1; i++) { ListNode tmp = mergeList(lists[i], lists[i + 1]); lists[i + 1] = tmp; } return lists[lists.length - 1]; }
private ListNode mergeList(ListNode node1,ListNode node2){ ListNode dummy = new ListNode(-1); ListNode p = node1,q = node2,t = dummy; while(p!=null&&q!=null){ if(p.val<q.val){ t.next = p; t = t.next; p = p.next; }else { t.next = q; t = t.next; q = q.next; } } t.next = (q==null) ? p : q; return dummy.next; } }
|
两两合并的时间复杂度为O(n),对于K个链表要合并K次,时间复杂度为O(kn)。
但是当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 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
class Solution { public ListNode mergeKLists(ListNode[] lists) { Queue<ListNode> pq = new PriorityQueue<>((v1, v2) -> v1.val - v2.val);
for (ListNode p : lists) { if (p != null) pq.offer(p); }
ListNode dummy = new ListNode(-1); ListNode t = dummy;
while (!pq.isEmpty()) { ListNode p = pq.poll(); t.next = p; t = t.next; if (p.next != null) pq.offer(p.next); }
return dummy.next; } }
|
为什么要用优先队列?
优先队列(最小堆)能够快速获取和移除当前最小值,这是因为我们希望每次从多个链表中获取最小节点并将其添加到结果链表中。使用优先队列可以确保这个过程高效进行。
时间复杂度
使用优先队列的时间复杂度分析:
- 将所有链表头节点加入优先队列的时间复杂度是 O(K log K),其中 K 是链表的数量。
- 合并链表过程中,每次操作都是在优先队列中插入和删除节点。总共有 N 个节点需要操作,每次插入或删除的操作时间复杂度是 O(log K),因此总时间复杂度是 O(N log 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
| class Solution { public int removeElement(int[] nums, int val) { int n = nums.length; int head = 0; int tail = n - 1; while (head <= tail) { if (nums[head] == val) { while (head < tail && nums[tail] == val) { tail--; } nums[head] = nums[tail]; tail--; } if (nums[head] != val) { head++; } } return head; } }
|
注意边界情况
难度:简单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Solution { public int removeDuplicates(int[] nums) { int len = nums.length; int idx = 0; int p = 0; while(p < len) { if(nums[p] != nums[idx]) { nums[++idx] = nums[p]; } p++; } return idx + 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
| class Solution { public int removeDuplicates(int[] nums) { int len = nums.length; int idx = 0; int p = 1; int count = 1; while(p < len) { if(nums[p] == nums[idx]) { count++; if(count <= 2) { nums[++idx] = nums[p]; } } else { nums[++idx] = nums[p]; count = 1; } p++; } return idx + 1; } }
|
这两个问题虽然都涉及从排序数组中删除重复项,但它们的具体要求不同:
题目要求的不同点
- 上一个题目(保留唯一元素):
- 题目要求从排序数组中删除重复项,使得每个元素只出现一次,并返回新的数组长度。
- 当前题目(最多保留两个重复元素):
- 题目要求从排序数组中删除重复项,使得每个元素最多出现两次,并返回新的数组长度。
解决方法的不同点
上一个题目(保留唯一元素):
- 使用一个指针
idx
来跟踪已处理的数组位置。
- 使用另一个指针
p
来遍历整个数组。
- 如果
nums[p]
不等于 nums[idx]
,则将 nums[p]
放到 idx
的下一个位置,并更新 idx
。
- 最终返回
idx + 1
作为新的数组长度。
当前题目(最多保留两个重复元素):
同样使用指针 idx
和 p
。
使用一个计数器 count
来记录当前元素出现的次数。
如果 nums[p]
等于 nums[idx]
,则增加 count
。
- 如果
count
小于或等于2,则将 nums[p]
放到 idx
的下一个位置,并更新 idx
。(count
意味idx
所指的数在新数组中已经出现的次数)
如果 nums[p]
不等于 nums[idx]
,则将 nums[p]
放到 idx
的下一个位置,重置 count
为1,并更新 idx
。
最终返回 idx + 1
作为新的数组长度。
有更简单的写法,不需要用count变量记录出现次数:
1 2 3 4 5 6 7 8 9 10 11 12
| class Solution { public int removeDuplicates(int[] nums) { int len = nums.length; int idx = 0; for(int i=0;i<len;i++){ if(idx<2||nums[idx-2]!=nums[i]){ nums[idx++] = nums[i]; } } return idx; } }
|