反转一个单链表
输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
思路:利用一趟遍历
需要的条件:1、当前节点 2、当前节点的前驱节点 3、当前节点的后继节点
-----------------------------python实现------------------------------
class NodeList():
def __init__(self,x):
self.value= x
self.next = null
class Demo():
def reverseList(self,head):
"""
@prev :单前节点的前驱节点
@cur:当前节点
当前节点后继几点,可表示为cur.next
"""
prev,cur = null,head
while(cur){
// 本语句利用了python的语法糖,一条语句完成了 同时赋值
// 将当前节点cur,存储为下一个节点的prev,在下一次循环中使用
// 将当前节点的后继节点cur.next ,存储为下一个节点的当前节点
// 将当前节点的前驱节点,变成后继节点,完成反转
prev,cur,cur,next = cur,cur.next,prev
}
// 遍历到最后一个节点时,最终是由prev存储这个节点的,注意这点不要写成cur,这时cur存储的是null
ruturn prev
-----------------------------java实现-------------------------
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Demo{
public ListNode reverseList(ListNode head) {
ListNode cur = head;
ListNode prev = null;
while(cur!=null){
/*
java语言没有类似python中一同赋值的语法糖,所以这里要注意一些细节,也就是赋值操作的执行顺序
1、首先将当前节点的后继节点缓存起来
2、将前驱节点prev 赋值给后继节点
3、将当前节点保存为下一个待遍历节点的前驱节点
4、更新当前节点,继续下一次遍历
以上4步可以看出,只有第2步真正的完成当前节点指针的指向,第1,3,4步骤是在为下一个节点作准备工作
*/
ListNode temp = cur.next; // 缓存当前节点的后继节点
cur.next = prev; // 改变当前节点指针的方向
prev = cur; // 更新前驱节点(当前操作的节点,是下一个节点的前驱节点),为下一次遍历做准备
cur = temp; // 更新cur,为下一次遍历做准备
}
return prev; //这里放回的是prev,遍历最后一个节点时,该节点被赋值给了prev, 而cur被赋值为null
}
}
遍历解法复杂度:
时间负杂度:O(n)
空间复杂度:O(1)
--------------------------------递归实现(java)-----------------------------------------------------------------
class Demo(){
public ListNode reverseList(ListNode head){
/*
1.递归的结束条件:碰到最后一个节点,即head.next ==null,结束递归
2.有效的反转动作:代码(1)不断的把当前节点 赋值给 其后继节点的next指针
3、反转后,末尾节点要指向null:代码(2)不断的把当前节点的next指针指向null
4、 反转后链表的头节点,通过last变量,在最后一次递归迭代时被赋值;然后,按照递归的层次,一层层的返回,知道递归完成。
所以,需要考虑的只有4各方面———— 1、递归结束条件/2、反转动作/3、获得反转后的头结点/4、反转后 末尾节点 要指向null
另外,在递归过程中,不断的进行入栈操作,所有遍历过的节点在栈中都有保存
*/
if(head == null){
return head;
}
ListNode headAfterReverse = reverseList(head.next);
head.next.next = head;//(1)
head.next = null; //(2) 链表的结尾要执行null
return last;
}
}
递归解法复杂度:
时间负杂度:O(n)
空间复杂度:O(n),堆栈内需要存储遍历过的所有节点
思考:
如果反转链表前N个节点呢?会发生哪些细微的变化?
1、递归结束条件,变成 n==1
2、反转后的尾节点要指向 第n+1个节点,所以需要一个额外的变量存贮这个节点;
class Demo(){
ListNode temp = null;
public ListNode reverseListN(head,n){
if(n==1){
temp = head.next;
return head;
}
ListNode headAfterReverse = reverseListN(head.next,n-1);
head.next.next = head;
head.next = temp;
}
}
思考:
如果反转链表的一部分呢,有哪些细节需要考虑?
题目;
反转从位置 m 到 n 的链表,1 ≤ m ≤ n ≤ 链表长度。
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
使用一趟扫描完成反转
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
使用一趟扫描完成反转
解法:递归
在反转链表前N个元素的基础上,进一步递归
1、递归结束条件 m==1
2、第m-1个节点的指针,要指向反转部分的头节点
3、在前m-2个个节点的递归中,都是返回当前节点;在第m-1个节点的递归中,返回的是反转链表部分的头结点;
class Demo(){
public ListNode reverseListBetween(head,m,n){
if(m==1){
return reverseListN(head,n);
}
// 一直到反转的起始节点(第m节点),触发 reverseListN
head.next = reverseListBetween(head.next,m-1,n-1);
return head;
}
}