[LeetCode] 143. Reorder List

32 篇文章 0 订阅
19 篇文章 0 订阅

原题链接: https://leetcode.com/problems/reorder-list/

1. 题目介绍

Given a singly linked list L: L0→L1→…→Ln-1→Ln,
reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→…

You may not modify the values in the list’s nodes, only nodes itself may be changed.

给出一个单项链表,L0→L1→…→Ln-1→Ln ,需要将其重新排列顺序,变成: L0→Ln→L1→Ln-1→L2→Ln-2→…

你不可以改变链表的节点的val,只能改变他们的连接关系。

Example 1:

Given 1->2->3->4, reorder it to 1->4->2->3.

Example 2:

Given 1->2->3->4->5, reorder it to 1->5->2->4->3.

2. 解题思路

这道题目真是一道好题,属于链表练习的综合题。
这道题涉及三种针对链表的操作:

  1. 如何从中间将链表一分为二?
  2. 如何反转一个链表?
  3. 如何拼接两个链表?

这三个问题如果单独拿出来考察,都可以出三个题目了,事实上LeetCode中还真有对应的题目。

  1. 如何从中间将链表一分为二?
    ------使用快慢双指针法,该方法在 141. Linked List Cycle142. Linked List Cycle II109. Convert Sorted List to Binary Search Tree 中都使用过。
  2. 如何反转一个链表?
    ------反转链表使用的是 206. Reverse Linked List 中反转链表的方法
  3. 如何拼接两个链表?
    ------使用了和 21. Merge Two Sorted Lists2. Add Two Numbers 中类似的合并链表的方法

因此强烈建议先将上述题目做完后,再研究本题。


注: 接下来的内容参考了 https://www.cnblogs.com/love-yh/p/7017991.html ,该文讲解得非常好,我用的图片也是取自 https://www.cnblogs.com/love-yh/p/7017991.html 。有兴趣的读者可以直接去看原文。

反转以后的链表,从左往右是: L0→Ln→L1→Ln-1→L2→Ln-2→… ,首先想到是将链表 L 按照这个规律重新赋值一遍,但题中说不能改变结点的值;然后想到将链表反转然后重新连接起来,如1->2->3->4:
在这里插入图片描述
按虚线那样走,形成1->4->2->3->3->2->4->1。如果这样做,前面的1->4->2->3就是我们想得到的结果。也就是需要将得到的链表分成相等的两段,取前一段。这样做需要统计链表节点的总数,比较麻烦。我们不如在一开始就将链表一分为二,然后反转后半段,最后重新连接起来,依旧是1->2->3->4。
在这里插入图片描述
这样思路就出来了,分三步走:
第一步,将链表一分为二,用到快慢指针
第二步,反转第二部分
第三步,将两链表拼接起来

第1步,将链表一分为二

使用快慢指针将链表分成两段。假设有一个快指针fast,一个慢指针slow。快指针的速度是慢指针的两倍,它们都从head节点向后跑。当快指针到达链表尾部的时候,慢指针就会刚好在链表的中间,于是就可以从中间将链表一分为二了。
采用这种方式会导致在链表结点个数为奇数的情况下,后半段的个数比前半段多一个。

第2步,反转链表

在遍历链表时,将当前节点的 next 指针改为指向前一个元素。由于节点没有引用其上一个节点,因此必须事先存储其前一个节点。在更改 next 之前,还需要另一个指针来存储下一个节点。而且在反转以后,要在新的链表结尾加上next = NULL。

第3步,拼接两个链表

在链表结点个数为奇数的情况下,后半段的个数会比前半段多一个。所以在将两子链表连接起来的时候,要注意判断反转的后半段是否已经结束,没有,则连接上剩余部分即可。

实现代码

class Solution {
    public void reorderList(ListNode head) {
	
		if(head == null || head.next == null){
			return;
		}
        
        //第一步:利用快慢指针法,将链表从中间一分为二
        ListNode fast = head;
        ListNode slow = head;
        ListNode slowPre = null;
        while(fast != null && fast.next != null){
        	fast = fast.next.next;
        	slowPre = slow;
        	slow = slow.next;
        }
        slowPre.next = null;
        //一分为二后,链表的两段,前一段从head开始,到slowPre结束
        //后一段从slow开始,到链表尾部结束
 
 
        //第二步,反转后一段链表,也就是从slow开始的那段链表
        //反转的方法是让每一个节点的next都指向它前面的节点
        ListNode cur = slow;
        ListNode before = null;
        while(cur != null){
        	ListNode behind = cur.next;
        	cur.next = before;
        	before = cur;
        	cur = behind;
        }
        //反转后的链表,第一个节点是before所指的节点
        
        
        //第三步,合并两个链表
        ListNode l1 = head;
        ListNode l2 = before;
        ListNode assist = new ListNode(0);
        while(l1 != null || l1 != null){
        	if(l1 != null){
        		assist.next = l1;
        		l1 = l1.next;
  				assist = assist.next;
        	}
        	
        	if(l2 != null){
        		assist.next = l2;
        		l2 = l2.next;
        		assist = assist.next;
        	}  	
        }
        
    }
}

3. 参考资料

https://www.cnblogs.com/love-yh/p/7017991.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值