剑指offer反转链表

今日第二个题,互相监督互相学习哦~,上题:

题目描述
输入一个链表,反转链表后,输出新链表的表头。

分析:链表这个东西我感觉真的是一个字——绕,绕的你脑壳疼,左绕右绕就别想出来了~~~

题目所给的是单链表,想了一下反转后的样子:最后一个结点指向倒数第二个,倒数第二个指向倒数第三个,......,第二个指向第一个,第一个指向null; 知道了反转后各个结点指向哪之后,就需要开始调整每个结点的next指针。 
这就需要把结点挨个从链表上摘下来,做调整; 这个调整过程需要两个指针辅助:pre记录其前一个结点位置,好让该结点的next指针指向前一个结点,但是在指向前一个结点前需要用一个指针p记录后一个结点地址,避免结点丢失。 
举个栗子: 
以head结点为例步骤如下: 
    1.反转后head是指向null,所以未反转的时候其前一个结点应该是null,初始化pre指针为null;
    2.用p指针记录head的下一个结点head.next; 
    3.从链表上摘下head,即让head.next指向pre; 
    4.此时已完成head结点的摘取及与前一个节点的连接,则我们需要操作下一个结点:故需移动pre和      head,让pre指向head,head指向下一个节点。 
    重复这四个操作直到head走完原链表,指向null时,循环结束,返回pre。 

最后有附图和说明。

代码:
/*
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)return null;
        ListNode pre=null;
        ListNode p;
        while(head!=null){
            p=head.next;
            head.next=pre;
            pre=head;
            head=p;
        }
        return pre;
    }
}
牛客运行通过
运行时间:16ms
运行内存:9664Kb
这是通过迭代的方法实现的,一般迭代其实可以转化为递归,所以接下来用递归的方法实现,其实都一样。

方法二:
代码:
/*
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;
        ListNode pre=ReverseList(head.next);
        head.next.next=head;
        head.next=null;
        return pre;
    }
}
牛客运行通过
运行时间:17ms
运行内存:9548Kb
这两种方法最好都要掌握,因为不知道面试官会问你哪个?哪个晓得~~~


再来一题,直接放在一起吧。
k个一组反转链表,不足k不反转。
题目描述
将给出的链表中的节点每k个一组翻转,返回翻转后的链表 
如果链表中的节点数不是k的倍数,将最后剩下的节点保持原样 
你不能更改节点中的值,只能更改节点本身。 
只允许使用常数级的空间 
例如: 
给定的链表是1->2->3->4->5 
对于 k = 2, 你应该返回 2->1->4->3->5 
对于 k = 3, y你应该返回 3->2->1->4->5 

这可以说是上一道题的扩展,但是又比上一题难挺多的,因为不仅需要进行反转,还需要k一组才反转,后面的还不反转,神奇吧,不知道谁想出来的。
分析:
既然是要分成小组再判断,则每个小组需要设置两个指针pre头和tail尾,来限定这个小分组。
然后在利用上一题的反转实现。但是这里需要保存每一个小组的头和尾,因为还和前一个或者后一个小组有联系。
代码:
import java.util.*;

/*
 * public class ListNode {
 *   int val;
 *   ListNode next = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param head ListNode类 
     * @param k int整型 
     * @return ListNode类
     */
    public static ListNode reverseKGroup(ListNode head, int k) {
        if(head == null || head.next == null || k < 2) return head;
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode pre = dummy, cur = head, temp;
        int len = 0;
        while (head != null) {
            len ++ ;
            head = head.next;
        }        
        for (int i = 0; i < len / k; i ++ ) {
            for (int j = 1; j < k; j ++ ) {
                temp = cur.next;
                cur.next = temp.next;
                temp.next = pre.next;
                pre.next = temp;
            }
            pre = cur;
            cur = cur.next;
        }        
        return dummy.next;
    }
}
牛客运行通过
运行时间:222ms
运行内存:14388Kb
因为涉及到了两层循环,先求链表的长度从而得到要反转的次数,然后再进行k个一组反转。

还有利用两个指针的,就是我在分析中所提到的;
代码:
public class Solution {
    public static ListNode reverseKGroup(ListNode head, int k) {
        if(head == null || head.next == null || k < 2) return head;
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        head=dummy;
        ListNode p=head,q=p;
        ListNode cur=null;
        while(q!=null){
            for(int i=0;i<k&&q!=null;i++){
                q=q.next;
            }
            if(q==null)break;
            cur=p.next;
            for(int i=0;i<k-1;i++){
                ListNode temp=cur.next;  //记录要改变节点的下一个节点,防止断链
                cur.next=temp.next;      //将当前指针指向的下一个节点指向所要交换节点的下一个节点,为了每次k组结束,上一个组和下一个组还有联系。
                temp.next=p.next;        //将第二个节点的指针指向第一个,完成两个节点的交换。
                p.next=temp;             //记得将头结点的指向进行改变。
            }
            p=cur;                       //cur已经指向下一个了,所以直接就是cur赋值;
            q=p;
        }
        return head.next;
    }
}
牛客运行通过
运行时间:220ms
运行内存:14576Kb
链表的题真滴真滴是在是太难了~~~~

欢迎各位互相交流指正哦~~

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值