《剑指Offer》Java刷题 NO.15 反转链表(链表、多指针、递归)
传送门:《剑指Offer刷题总目录》
时间:2020-02-24
题目:
输入一个链表,反转链表后,输出新链表的表头。
思路:
1.非递归方法:
- 除了传进来的头节点作为当前结点之外,需要两个空结点,pre和next
- pre记录当前指针的前一个指针,反转之后应该是当前节点指向pre
- next结点用来作为临时结点存储当前结点的下一个,这样当前结点反向以后才不会断掉找不到后面的结点
循环,知道当前节点的下一个是null时,当前结点就是反转后的列表的头结点
2.递归方法:(我想不出来啊…哭了…递归太难了) - 递归终结条件:当前结点.next=null,那么当前结点(原链表的尾节点)就是反转后链表的头结点,直接返回当前结点
- 递归语句:ListNode newHead=reverseList(head.next)(递归处理除头结点之外后面的链表);
- 返回语句:return newHead;
这样就能保证一步一步调用之后的返回值始终是原链表的尾节点,也就是反转后新链表的头结点 - 每次递归时先用temp结点把当前结点.next保存下来, 每次循环时把当前结点的下一个(temp)指向当前结点,然后当前结点下一个指向null
扩展:
每隔k个元素进行一次反转
Java代码:
/**
* 输入一个链表,反转链表后,输出新链表的表头。
*/
public class ReverseList {
/**
*非递归方法
*/
public static ListNode reverseList1(ListNode head){
if(head==null) return null;//先判断链表是否为空
ListNode pre=null;
ListNode next=null;//分别记录当前结点的前一个和后一个
while(head!=null){//当前结点为空时说明当前结点的pre已经是尾节点了,返回pre
next=head.next;//先把下一个节点保存下来,不然断链之后会找不到下一个
head.next=pre;//当前结点的下一个指向pre(前一个结点)
pre=head;
head=next;//当前结点和pre都后移一个
//最后的head(null)是和前面已经反转好的链表没连接起来的,所以需要返回pre
}
return pre;
}
/**
* 递归方法
*/
public static ListNode reverseList2(ListNode head){
if(head==null||head.next==null) return head;
//head==null主要是为了判断初始链表是否为空,head.next==null是为了满足递归终止条件
ListNode temp=head.next;
ListNode newHead=reverseList2(temp);
temp.next=head;
head.next=null;
return newHead;
//假设一共0~n-1个结点
//最后:head=n-2,temp=n-1,返回newHead=n-1,null<--(n-2)<--(n-1)
//弹出上一个状态的值,head=n-3,temp=n-2,null<--(n-3)<--(n-2)<--(n-1)。。继续
}
}
class ListNode{
int val;
ListNode next=null;
ListNode(int val){
this.val=val;
}
public void setNext(ListNode node){
this.next=node;
}
public static void print(ListNode head){
while(head!=null){
System.out.println(head.val);
head=head.next;
}
}
}