剑指 Offer 24. 反转链表
题目链接:https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof
本文给出两种语言的解法:1.JavaScript (递归解法、迭代解法) 2.Java(迭代解法)
一、JavaScript(递归解法、迭代解法)
1.递归解法
1 /** 2 * 先处理链头为空的情况(链为空) 3 */ 4 var reverseList = function(head) { 5 if(head == null) return head; 6 } 7 /** 8 * 第一步就是划分小问题:需要将当前结点的下一个结点指向当前结点; ...(1) 9 * 也就是head.next.next = head; ...(1) 10 * 但在操作(1)之前,必须解决下一个结点的小问题(也就是要对下一个结点进行操作1) 11 * 而解决小问题的方法就是reverseList(head) 12 * 因此: 13 */ 14 var reverseList = function(head) { 15 if(head == null) return head; 16 reverseList(head.next); 17 head.next.next = head; //...(1) 18 } 19 /** 20 * 第二步就是找递归出口:到什么时候就没有了下一个结点的小问题? 21 * 当下一个结点为空时:head.next == null 22 * 注意:当到了递归出口(下一个结点为空)时,所传入的结点将会是新的头节点。 23 */ 24 var reverseList = function(head) { 25 if(head == null || head.next == null) return head; 26 reverseList(head.next); 27 head.next.next = head; //...(1) 28 } 29 /** 30 * 因为题目要求返回链表反转后的新头结点, 31 * 那么需要将新头结点层层传递出去:保留递归出口返回的新头结点逐层返回 32 */ 33 var reverseList = function(head) { 34 if(head == null || head.next == null) return head; 35 let newHead = reverseList(head.next); 36 head.next.next = head; //...(1) 37 return newHead; 38 } 39 /** 40 * 到此,除了旧头结点之外的结点的next都已经完成重新指向。 41 * 根据题目要求,旧头结点将指向null; 42 * 在代码中对每个结点都进行next赋值null操作:head.next = null; 43 * 虽然对每个结点都进行了该操作(本不需要的操作), 44 * 但除了旧头结点外的结点的next都将会重新指向(在递归的外层会重新指向)。 45 */ 46 var reverseList = function(head) { 47 if(head == null || head.next == null) return head; 48 let newHead = reverseList(head.next); 49 head.next.next = head; //...(1) 50 head.next = null; 51 return newHead; 52 }
2.迭代解法
由头结点始,逐个访问结点并对其操作:将其next指向前一个结点(在此操作前需保留下一个结点的引用),
在该操作有一个问题就是在访问当前结点时,如何指向前一个结点?解决手段就是在访问第二个结点之前先保存第一个结点(也就是在遍历下一个结点之前保存当前结点的引用)。
1 /** 2 * @param {ListNode} head 3 * @return {ListNode} 4 */ 5 var reverseList = function(head) { 6 let tem1 = null; 7 let tem2 = null; 8 let index = head; 9 while(index !== null) { 10 tem2 = index.next; 11 index.next = tem1; 12 tem1 = index; 13 index = tem2; 14 } 15 return tem1; 16 };
二、Java(迭代解法)

1 /** 2 * @author 不乏理想的三师弟 3 * 解法一 也就是题解中的迭代解法 4 * 原理: 5 * 由头结点开始,遍历链表;同时完成两件事: 6 * 1、将当前结点的next指向前一个结点; 7 * 2、并且对下一个结点也能做第一件事; 8 * 9 * 用head代表当前结点, 10 * 要做的第一件事:head.next 指向前一个结点; 11 * 因此要有临时变量pre保存前一个结点;(初始时pre当然为null) 12 * 13 * (1)head.next = pre; // 将当前结点的next指向前一个结点; 14 * 15 * 当执行语句(1)时,下一个结点的引用会被抹去; 16 * 因此在执行语句(1)之前,要另一个临时变量tem保存下一个结点:tem = head.next; 17 * 执行完语句(1)后,就准备操作下一个结点; 18 * 19 * (2)head = tem; // 准备操作下一个结点 20 * 21 * 在执行语句(2)时,原本head的结点就会被抹去; 22 * 因此在执行语句(2)之前,要将head结点赋值给pre:pre = head; 23 * 24 * 语句顺序如下: 25 * head.next = pre; (1) 26 * tem = head.next; 27 * pre = head; 28 * head = tem; (2) 29 */ 30 public static ListNode reverseList(ListNode head) { 31 if(head == null) return null; 32 ListNode pre = null, tem = null; 33 34 // 为了不打乱语句的逻辑顺序就用break来跳出循环 35 while (true) { 36 tem = head.next; 37 head.next = pre; 38 if (tem == null) { 39 // beak要在head.next = pre后执行; 40 // 因为在最后一刻还要将当前结点的next指向前一个结点 41 break; 42 } 43 pre = head; 44 head = tem; 45 } 46 return head; 47 }

浙公网安备 33010602011771号