wl413911

反转链表

反转一个单链表

输入: 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
使用一趟扫描完成反转
解法:递归
在反转链表前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;
 
}
}

 
 
 
 
 
 
 
 

posted on 2020-05-19 18:29  wl413911  阅读(177)  评论(0编辑  收藏  举报