给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
给定链表 1->2->3->4, 重新排列为 1->4->2->3.
示例 2:
给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.
这个题其实就是将后面的链表的节点依次进行往前面的空里进行插入。其实就是插空法。
分三步:
1、找到链表的中点
2、反转链表 因为链表的特性 我们只能从后往前依次插入 这样需要从链表的最后开始
这里需要注意的一点 我们对链表的反转其实只需要从中间结点的下一个开始就行,因为中间结点最后都会挂在重排后的最后一个 我们就不需要对齐进行反转。
3、一次进行往前的结点插入。
public void reorderList(ListNode head) {
if (null == head || null == head.next) {
return;
}
//1、找链表中点
ListNode mid = findMid(head);
/*2、反转后半部分链表
后半部分的开始反转的点 这个地方其实是要注意的 我们是从找到的中点
的下一个结点开始反转 因为不管链表是奇数个结点还是偶数个结点 中间结点是不需要进行往前插入的
所以我们只需要把需要进行插入的反转 并且将mid与后面的断开 这个实际就将链表分为两部分了
*/
ListNode reverseNeed = mid.next;
mid.next = null;
ListNode needInsert = reverseList(reverseNeed);
//3、往前依次插入
ListNode leftInserted = head;
ListNode leftInsertedNext = null;
ListNode needInsertNext = null;
while(null != needInsert){
//先将要插入的结点 和左边插入的结点的下一个都保存起来
leftInsertedNext = leftInserted.next;
needInsertNext = needInsert.next;
//插入结点 改变结点的指向
leftInserted.next = needInsert;
needInsert.next = leftInsertedNext;
//分别往前走一步 继续循环
leftInserted = leftInsertedNext;
needInsert = needInsertNext;
}
}
private ListNode findMid(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while(null != fast && null != fast.next){
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
//反转链表
private ListNode reverseList(ListNode head){
ListNode cur = head;
ListNode pre = null;
ListNode next = null;
while(null != cur){
//1 保存下一个结点
next = cur.next;
//2 反转结点 改变指向
cur.next = pre;
//继续下一个结点
pre = cur;
cur = next;
}
return pre;
}