leetcode-反转链表(方法总结)

题目描述:给定一个链表,将链表进行反转。

示例1:

输入:1->2->3->4->5->NULL

输出: 5->4->3->2->1->NULL

题目理解:首先先接收一个链表,然后根据链表的性能进行反转。

解法1:借助栈的后进先出的功能,先扫描一遍链表,在栈中保存每个节点的值,然后再从头到尾遍历,将栈中的元素按次序弹出赋给链表的节点,时空复杂度都是O(n).

package TEST;


import java.util.Stack;

/**
 * @Author:
 * @Description:反转链表;
 * @Date: Created in 2018/9/3 23:14
 * @Pram:
 */


public class Node {
    private Node next;
    private String val;
    public Node(String val){
        this.val = val;//节点的值;
    }

    public Node() {

    }

    public static void ReverseList(Node node){
        Stack<Node> stack = new Stack<Node>();//定义一个栈;
        while(node!=null){//链表不为空就开始遍历;
            stack.push(node);//入栈;
            node = node.next;

        }
        while(!stack.empty()){
            Node temp = stack.pop();
            System.out.println(temp.val);
        }

    }
    public static void main(String[] args) {

        Node node1 = new Node("A");
        Node node2 = new Node("B");
        Node node3 = new Node("C");

        node1.next = node2;
        node2.next = node3;

        System.out.println("链表反转后是:");
        ReverseList(node1);
        node1.next = null;
    }
}

解法2:可以做到in-place的反转。链表反转后,实际上只是中间节点的指针反转,并且反转后原来的链表的头节点的下一个节点应该为null,而反转后链表的头节点为原来链表的结尾处的尾节点。从头节点开始处理每两个节点之间的指针将其反转过来,然后处理接校来的指针,直到尾节点结束。并设置新的链表的头节点即可。

public class ListNode{
    int val;
    ListNode next;
    ListNode(int x) {
       val = x;
    }
}
public class Solution{
     public ListNode reserseList(ListNode head){
            if(head == null || head.next == null){
return head;
    }
      ListNode next = head.next;
      head.next = null;
      while(next.next != null){
           ListNode node = next.next;
           next.next = head;
           head = next;
           next = node;
    }
next.next = head;
return next;

}

} 

解法3--迭代:首先,肯定知道最后返回的结果是输入链表的链表尾节点。但先找到尾节点是很难继续实现的,因为链表没有办法高效获取前驱。往往这类问题很多时候都要想到建立一个新的节点,之后在遍历输入的时候重新组织节点顺序,将节点挂在新节点上。所以高效的做法是在遍历链表的过程中,一个一个的把输入链表的节点放到一个新的链表头部。所以思路就是建立一个新的链表头,每次遍历输入链表的节点都把他放到新链表的头部,这样遍历完成后就获得了反转的链表。详细代码注释见下。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode first = head;
        ListNode reverseHead = null; //建立一个新的节点用来存放结果
        while (first != null) { //遍历输入链表,开始处理每一个节点
            ListNode second = first.next; //先处理第一个节点first,所以需要一个指针来存储first的后继
            first.next = reverseHead; //将first放到新链表头节点的头部
            reverseHead = first; //移动新链表的头指针,让它始终指向新链表头部
            first = second; //继续处理原链表的节点,即之前指针存放的后继,循环往复
        }
        return reverseHead;
    }
}

解法4--递归:

递归:

每次想着用递归解法我习惯于用数学归纳法的思维去思考。先想输入规模最小的情况,再想比较general的情况。就本题来说,如果输入的是null或者单节点链表,必然是返回其本身。如果至少有两个节点,那么才开始递归。想一下,递归后的结果一定是一个规模更小问题的结果。即如果输入有k个节点,那么递归调用程序,输入原链表第二个节点所返回的结果,是一个反转后的,拥有k-1个节点的链表的首节点 —— 规模更小的问题的结果。那么如果把这个递归调用后返回的头节点所指向链表的尾节点的next域,指向被调用的节点的前驱,就相当反转了k个节点的链表。即利用k-1的结果去完成了k的问题。所以想到这里,在递归函数里要做的就是三件事:第一,记录即将被递归调用节点的前驱(或者换句话说,建立个新的节点指向输入的下一个节点,之后递归调用那个新节点);第二,递归调用输入的下一个节点;第三,将返回结果的末尾指向记录好的前驱节点,完成反转。

这里需要注意的只有第三步,如何找到返回的结果链表的末尾。还是要回归到递归的本质,即返回的结果是一个已经反转完成的链表的首节点。反转完成的意思就是我们输入一个以节点S为头结点,节点E为尾结点的链表,那么调用后返回的节点是E,而S经过调用后变成了尾节点。即,递归调用时的输入本身,即是调用完成后我们需要的尾节点!所以我们并不需要每一次都去寻找递归调用后结果的尾节点,只需要直接利用递归调用的输入即可,因为这个输入就是调用完成的尾节点。详细代码注释见下。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) return head; //处理最小输入的情况,即空链表和单节点链表
        ListNode second = head.next; //将即将被调用的下一个节点分离,即将下一个调用的输入存在second里
        ListNode reverseHead = reverseList(second); //将调用后的结果存储,这个结果就是最终结果。之后利用递归,调用刚才存好的输入
        second.next = head; //上面一步的调用已经完成以second为首的链表的反转,所以现在second变成了反转完成后的尾节点
                 //把这个尾节点的next指向一开始输入的前驱,即head,完成整个链表反转
        head.next = null; //最开始的头节点要变成尾节点,即在后面补null使链表终结
        return reverseHead; 
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值