反转链表
递归方法套路:
- 明确递归函数的定义
1.1 输入是什么?
1.2 要做什么?
1.3 输出是什么?
利用这个定义推导最终结果,绝不要跳入递归的细节。 - 明确base case(跳出条件)
- 最后的处理
//递归
class Solution {
// 1. 明确递归函数的定义
// 1.1 输入是什么? 一个链表节点head
// 1.2 要做什么? 将「以 head 为起点」的链表反转
// 1.3 输出是什么? 返回反转之后的头结点
public ListNode reverseList(ListNode head) {
//2. 明确base case(跳出条件)
if(head == null || head.next == null) return head;
ListNode last = reverseList(head.next); //last就是输出
// 3. 最后的处理
head.next.next = head;
head.next = null;
return last;
}
}
//迭代
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null) return null;
ListNode pre = null, cur = head, tmp = null;
while(cur != null){
tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
}
反转链表II
class Solution {
public ListNode successor = null;
public ListNode reverseBetween(ListNode head, int m, int n) {
if(head == null) return head;
if(m == 1){
return reverseN(head, n);
}
//向后遍历到反转的起点,调用reverseN()
head.next = reverseBetween(head.next, m-1, n-1);
return head;
}
//1.明确递归函数定义,reverseN(),反转链表的前n个元素,输出返回 反转后的节点
public ListNode reverseN(ListNode head, int n){
//2.明确base case,递归到最后的情况
if(n == 1){
successor = head.next;//找到后继节点
return head;
}
ListNode last = reverseN(head.next, n-1);//反转长度为n-1,从head的下一个节点开始的链表
//3.最后的处理,拼接,三部分:head,head下一个元素的反转链表(长度n-1),head的后继节点
head.next.next = head;
head.next = successor;
return last;
}
}
K 个一组翻转链表
class Solution {
//1.明确递归函数的定义:
//输入:一个链表的头节点,数字k
//做什么:K个一组反转链表
//输出:反转后的链表头节点
public ListNode reverseKGroup(ListNode head, int k) {
if(head == null) return null;
ListNode a = head, b = head;
//base case :数目不够k个,不进行反转操作,直接返回a
for(int i=0; i<k; i++){
if(b == null) return a;
b = b.next;
}
ListNode newHead = reverseK(a, b);//反转后新的头节点,1->2的话,现在就是2
a.next = reverseKGroup(b, k);//a还是1,a后继节点继续递归反转
return newHead;
}
//反转以a开头以
public ListNode reverseK(ListNode a, ListNode b){
if(a == null) return null;
ListNode pre = null, cur = a, tmp = null;
while(cur != b){
tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
}
判断回文链表
O(n)方法
//很巧妙的思路,利用递归后续遍历链表,其实就是 栈 的思想
class Solution {
public ListNode left = null;
public boolean isPalindrome(ListNode head) {
left = head;
return digui(head);
}
public boolean digui(ListNode right){
if(right == null) return true;
//boolean res = digui(right.next) && (left.val == right.val);
boolean res = digui(right.next);
//这里就是后续遍历的代码,递归结束弹栈的时候,会从后向前一次遍历链表的值
res = res&& (left.val == right.val);
left = left.next;
return res;
}
}
O(1)方法
//关键思想是利用快慢指针,找到最中间的对称点,将对称点后面的链表进行反转,然后进行遍历比较即可
//这样就是O(1)了
class Solution {
public boolean isPalindrome(ListNode head) {
ListNode slow = head, fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
if(fast != null) slow = slow.next;
ListNode left = head;
ListNode right = reverse(slow);
while(right != null){
if(left.val != right.val) return false;
else {
left = left.next;
right = right.next;
}
}
return true;
}
public ListNode reverse(ListNode head){
if(head == null) return head;
ListNode pre = null, cur = head, tmp = null;
while(cur != null){
tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
}