翻转链表合集

翻转链表大概可以分为如下四个,难度可以说是逐步增大的

  1. 翻转整个链表(递归和非递归)
  2. 翻转链表中的一部分
  3. 按k个一组进行翻转(从前往后)
  4. 按k个一组进行翻转(从后往前)

首先给出节点类定义

	class ListNode{
        int val;
        ListNode next;
        public ListNode(int val){
            this.val = val;
        }
    }

1. 翻转整个链表

  • 递归翻转整个链表,需要注意的是要记得head.next置为空,避免形成环路。
	public ListNode reverseListRecursive(ListNode head) {
        if(head == null || head.next == null)    return head;
        ListNode reversed = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return reversed;
    }
  • 迭代翻转链表,因为翻转后原本的头结点会变成尾结点,因此开始循环前需要将头结点的next置为空。
	public ListNode reverseList(ListNode head) {
        if(head == null)    return head;
        ListNode prev = head, cur = head.next;
        prev.next = null;
        while(cur != null){
            ListNode temp = cur.next;
            cur.next = prev;
            prev = cur;
            cur = temp;
        }
        return prev;
    }

结果如下:
在这里插入图片描述

2. 翻转部分链表

  翻转序号fromIndex到toIndex之间的所有结点,假设链表头结点序号为1,需要注意的是fromIndex和toIndex可能会超出链表长度,此时的话不进行任何操作,直接返回。对于链表问题,声明dummy头部结点有时会带来好处,这里的话可以避免当fromIndex为1时需要进行额外的判断。
  首先找到需要翻转的部分,然后进行翻转(迭代),需要注意的是翻转的时候,需要保存翻转链表的前缀结点fromPre和后缀结点toNext,以便于后面重新拼接。

	public ListNode reverseList(ListNode head, int fromIndex, int toIndex){
        if(fromIndex <= 0 || toIndex <= 0)  return head;
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode fromPre = null, from = null, to = null, toNext = null;
        ListNode cur = dummy;
        int count = 0;
        while(cur != null){
            if(count == fromIndex-1)    fromPre = cur;
            if(count == fromIndex)    from = cur;
            if(count == toIndex){
                to = cur;
                toNext = cur.next;
                break;
            }
            cur = cur.next;
            count++;
        }
        if(from == null || to == null)  return head;    //fromIndex或者toIndex超出链表长度时
        fromPre.next = null;
        to.next = null;

        //翻转from到to
        ListNode pre = from;
        cur = pre.next;
        pre.next = null;
        while(cur != null){
            ListNode temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;

        }
        fromPre.next = to;
        from.next = toNext;
        return dummy.next;
    }

翻转1号到4号的所有节点,最终结果如下所示
在这里插入图片描述

3. 从前往后按k个一组进行翻转

  相比于前一个问题,这次是要循环的翻转,同样的在每次翻转前需要保存翻转部分的前缀结点和后缀结点。

	public ListNode reverseListk(ListNode head, int k){      //每k个一组进行翻转(从前往后数)
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode cur = dummy, preStartk = null, startk = null, endk = null;
        int index = 0;
        boolean reverse = false;	//是否需要翻转
        while(cur != null){
            if(index == 0)  preStartk = cur;
            if(index == 1)  startk = cur;
            if(index == k){
                endk = cur;
                reverse = true;
            }
            cur = cur.next;
            index++;
            if(reverse){
                ListNode pre = startk, preNext = pre.next;
                while(preNext != cur){
                    ListNode temp = preNext.next;
                    preNext.next = pre;
                    pre = preNext;
                    preNext = temp;
                }
                preStartk.next = endk;
                startk.next = cur;
                //进行下一组翻转
                cur = startk;
                index = 0;
                reverse = false;
            }
        }
        return dummy.next;
    }

按3个一组(从前往后)进行翻转,结果如下
在这里插入图片描述

4. 从后往前按k个一组进行翻转

  说实话,第三个问题从前往后翻转k个已经是Leetcode中hard级别的题目了,这个可能更难一点。主要是要想到思路,可以分为三个步骤,这两个函数在上面的问题已经实现了。

  1. 翻转整个链表
  2. 从前往后按k个每组进行翻转
  3. 再一次翻转整个链表
	public ListNode reversedListk(ListNode head, int k){      //逆序k个一组进行翻转(从后往前数)
        head = reverseList(head);       //首先翻转整个链表
        head = reverseListk(head, k);   //然后从前往后按k个每组进行翻转
        head = reverseList(head);       //最后再翻转整个链表
        return head;
    }

按3个一组(从后往前)进行翻转,结果如下
在这里插入图片描述
其它的辅助代码如下所示

public static void main(String[] args) {
        ListNode head = new ListNode(1), cur = head;
        for(int i = 2;i<8;i++){
            ListNode temp = new ListNode(i);
            cur.next = temp;
            cur = temp;
        }

        ReverseList solution = new ReverseList();
        System.out.println("翻转前:"+solution.printListNode(head));

//        head = solution.reverseList(head);            //非递归翻转
//        head = solution.reverseListRecursive(head);   //递归翻转
//        head = solution.reverseList(head, 1, 4);      //只翻转from到to的结点
//        head = solution.reverseListk(head, 3);        //从前往后k个一组进行翻转
        head = solution.reversedListk(head, 3);     //从后往前k个一组进行翻转

        System.out.println("翻转后:"+solution.printListNode(head));
    }
    public String printListNode(ListNode head){
        StringBuffer s = new StringBuffer();
        ListNode cur = head;
        while(cur != null){
            s.append(cur.val).append(' ');
            cur = cur.next;
        }
        return s.toString();
    }

本文参考:我画了20张图,终于让女朋友学会了翻转链表

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值