剑指offer面试题24(java版):反转链表

welcome to my blog

剑指offer面试题24(java版):反转链表

题目描述

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null

最初版的代码如下, while循环中很不好, 其实从初始化Next就注定了这个while写不好了

不要根据初始化写代码, 而是根据分析问题时的步骤写代码

反转链表的子过程: 1.保存Curr.next 2.改变Curr.next的指向 3.向后移动Pre和Curr

上面的过程1,2,3需要对每个节点执行一次

第二次做 反转链表是基础,务必牢牢掌握。 循环中,存,变,更
  • 循环中:curr!=null
  • 存:存储当前节点的下一个节点
  • 变:改变当前节点的指向
  • 更:更新left,curr
  • right单独更新,left,curr一起更新
/*
循环中:
    先保存当前节点的下一个节点,
    再改变当前节点的指向,
    最后让left指向当前节点,让当前节点指向之前保存的下一个节点
*/
public class Solution {
    public ListNode ReverseList(ListNode head) {
        if(head == null || head.next == null) 
            return head;
        //三指针
        ListNode left=null, curr=head, right = null;
        while(curr != null){
            //保存下一个节点
            right = curr.next;
            //change direction
            curr.next = left;
            //update lef, right
            left = curr;
            curr = right;
        }
        return left;
    }
}
第二次做, 使用栈; 可能有助于理解递归?
import java.util.Stack;

public class Solution {
    public ListNode ReverseList(ListNode head) {
        if(head==null)
            return head;
        Stack<ListNode> s = new Stack<>();
        ListNode curr=head;
        while(curr!=null){
            s.push(curr);
            curr = curr.next;
        }
        curr = s.pop();
        ListNode newHead=curr, left;
        while(!s.isEmpty()){
            //
            left = s.pop();
            //
            curr.next = left;
            left.next = null;
            //
            curr = left;
        }
        return newHead;
    }
}
第二次做,递归版,感受递归过程; 注意:返回的是base case处碰到的节点
  • 递归函数返回新的头结点
  • head.next.next = head;改变了链表的指向
  • head.next = null; 让最后弹出的节点指向空
  • base case:主要用的是head.next == null判断当前是否是最后一个节点
  • 另一个head==null主要是Input check
  • 巧妙使用了栈:先入后出的特性
public class Solution {
    public ListNode ReverseList(ListNode head) {
        //base case
        if(head == null || head.next == null)
            return head;
        //
        ListNode newHead = ReverseList(head.next);
        head.next.next = head; // head.next.next,区分next的赋值操作和直接使用
        head.next = null; //这一句的作用是为了让最后弹出的节点指向null
        return newHead;
    }
}
第二次做, 递归版, 感受递归过程; 感受:left,curr一起更新, right单独更新; 感受:存,变,更
  • 递归函数返回的是反转后链表的头结点
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode ReverseList(ListNode head) {
        if(head==null || head.next==null)
            return head;
        //这里体现出:反转链表时,left,curr一起更新, right单独更新
        return Core(null, head);
    }
    public ListNode Core(ListNode left, ListNode curr){
        //base case
        if(curr == null) return left;
        //cun
        ListNode right = curr.next;
        //bian
        curr.next = left;
        //geng
        left = curr;
        curr = right;
        return Core(left, curr);
    }
}
public class Solution {
    public static ListNode ReverseList(ListNode head) {
        /*
        反转列表
        每次处理需要知道三个节点, Pre, Curr, Next
        其中,Curr要从第一个节点遍历到最后一个节点
        随之而来的就是,Pre,Next可能指向某个节点,也可能指向null
        */
        // input check;
        if (head == null || head.next == null)
            return head;
        //execute
        ListNode Pre = null;
        ListNode Curr = head;
        ListNode Next = head.next; // 注意Next的初始化
        while (Curr != null) { 
            Curr.next = Pre;
            Pre = Curr;
            Curr = Next;
            if(Curr == null)
                return Pre;
            Next = Next.next;
        }
        return Curr;
    }
}

笔记

  • 分析问题时找出的通用步骤, 适合于哪些节点?

改进后的代码(循环版)

public class Solution {
    public static ListNode ReverseList(ListNode head) {
        /*
        反转列表
        每次处理需要知道三个节点, Pre, Curr, Next
        其中,Curr要从第一个节点遍历到最后一个节点
        随之而来的就是,Pre,Next可能指向某个节点,也可能指向null
        */
        // input check;
        if (head == null || head.next == null)
            return head;
        //execute
        ListNode Pre = null;
        ListNode Curr = head;
        ListNode Next = null; // 注意Next的初始化
        while (Curr != null) { 
            Next = Curr.next; // 保存next
            Curr.next = Pre; // 反转
            Pre = Curr;
            Curr = Next; 
        }
        return Pre;
    }
}

递归版代码

public class Solution {
    public ListNode ReverseList(ListNode head) {
        /*
        反转列表
        每次处理需要知道三个节点, Pre, Curr, Next
        其中,Curr要从第一个节点遍历到最后一个节点
        随之而来的就是,Pre,Next可能指向某个节点,也可能指向null
        */
        // input check;
        if (head == null || head.next == null)
            return head;
        //execute
        return ReverseListCore(null, head);
    }
    
    public ListNode ReverseListCore(ListNode Pre, ListNode Curr){ // 没有必要在参数中加入Next, 这样当Curr为null时, 没有Next, 还得额外判断, 浪费时间
        // 递归终止条件
        if(Curr == null)
            return Pre;
        // execute
        ListNode Next = Curr.next; // 保存下一个节点
        Curr.next = Pre; // 改变指向
        return ReverseListCore(Curr, Next);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值