今天的三个题都是链表题诶,都是比较简单的题。
19. 删除链表的倒数第N个节点
题目要求最好是一次遍历。
类似题目在考研数据结构见过,也挺简单的。
因为链表我们不知道它的长度,一个指针的话必定需要两次遍历,一次判断长度,一次根据长度来找倒数第N个节点删除。
重要的是找出倒数第N个结点,删除就很简单了。
两个指针只需要一次遍历,分别设置两个指针 p
和 tmp
均指向头指针head
,一开始时,先让 tmp
向后移动 n 个位置,然后 p
和tmp
一起向后移动,当tmp.next
为 null
时 停止移动, 这时候 p.next
就是导数第N个结点,删除就好。
代码
public ListNode removeNthFromEnd(ListNode head,int n){
ListNode p = head;
ListNode tmp = head;
for (int i = 0; i < n; i++) tmp=tmp.next;
// n为链表长度的情况
if (tmp == null) return head.next;
while ( tmp.next != null){
tmp = tmp.next;
p = p.next;
}
p.next = p.next.next;
return head;
}
21. 合并两个有序链表
有序链表合并,也很简单,比较大小,小的插在前面就可以了。
题解里的递归算法没有想到,代码很简洁
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
}
else if (l2 == null) {
return l1;
}
else if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
}
else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
}
自己写的迭代的方式,就很啰嗦了:
public static ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) return l2;
if (l2 == null) return l1;
ListNode p1 = l1.val < l2.val ? l1 : l2;
ListNode per = null;
ListNode p2 = l2.val > l1.val ? l2 : l1;
while (p1 != null && p2 != null) {
if (p1.val <= p2.val) {
if (per != null)
per.next = p1;
per = p1;
p1 = p1.next;
} else {
per.next = p2;
per = p2;
p2 = p2.next;
}
}
if (p1 != null) per.next = p1;
if (p2 != null) per.next = p2;
return l1.val < l2.val ? l1 : l2;
}
23. 合并K个排序链表
官方题解很多种方法解决。
联系21题,递归的思想,只不过是把两个链表变成了多个链表,只需要找出多个链表表头中最小的即可。
每一次递归都需要找出最小的结点,一共是N次递归(最长链表长度),时间复杂度位O(nk),题解中提到,可以使用优先队列来优化找最小结点这部分,这样就可以把时间复杂度降到O(nlogk),不过我没改。
public ListNode mergeKLists(ListNode[] lists) {
int minIndex = -1;
int minVal = Integer.MAX_VALUE;
for (int i = 0; i < lists.length; i++) {
if (lists[i] != null && lists[i].val < minVal){
minVal = lists[i].val;
minIndex = i;
}
}
if (minIndex == -1) return lists[0];
else{
lists[minIndex] = lists[minIndex].next;
lists[minIndex].next = mergeKLists(lists);
return lists[minIndex];
}
}