反转类题目
1:反转链表
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
思路:
递归,当head==null 反转,关键点:head.next.next=head.next;
什么意思呢?
//如果链表是 1->2->3->4->5,那么此时的cur就是5
//而head是4,head的下一个是5,下下一个是空
//所以head.next.next 就是5->4
public ListNode reverseList(ListNode head) {
//递归终止条件是当前为空,或者下一个节点为空
if(head==null || head.next==null) {
return head;
}
//这里的cur就是最后一个节点
ListNode cur = reverseList(head.next);
//这里请配合动画演示理解
//如果链表是 1->2->3->4->5,那么此时的cur就是5
//而head是4,head的下一个是5,下下一个是空
//所以head.next.next 就是5->4
head.next.next = head;
//防止链表循环,需要将head.next设置为空
head.next = null;
//每层递归函数都返回cur,也就是最后一个节点
return cur;
}
2:反转指定n-m个节点
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
思路:分2类问题组合看待。第一类是反转指定n的链表,再解决反转m-n
1:反转指定n的链表
思路:跟“反转链表”类似,区别点在于终结条件是递归深度是n,得到2个链表
新链表反转,再把新链表的尾部接上第一个链表。
3:反转m-n
上面的反转n 其实就是反转1-n。所以将上面的反转n包装成递归。
ListNode reverseBetween(ListNode head, int m, int n) {
// base case
if (m == 1) {
return reverseN(head, n);
}
// 前进到反转的起点触发 base case
head.next = reverseBetween(head.next, m - 1, n - 1);
return head;
}
合并类题目
1:排序链表
归并排序
2:合并2个排序的链表
递归,逐个比较2个链表
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;
}
}
3:合并K个排序链表【困难】
分治思路,最小2个链表合并。同上。
移除节点
1:移除重复节点
思路1:先归并排序链表,再遍历一次,与前一个相同的跳过
思路2:双指针暴力遍历。第一个指针指向第一个数,第二个指针遍历剩余的数,如果有则将p1,指向p2的位置。最后保留一个。
2:链表中倒数第N个节点
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
解题思路:
双指针