NC2 重排链表
中等 通过率:25.94% 时间限制:1秒 空间限制:256M 知识点:链表
描述
将给定的单链表 L L L: L0→L1→…→Ln1→LnL_0→L_1→…→L_{n-1}→L_ nL0→L1→…→Ln1→Ln
重新排序为:L0→Ln→L1→Ln1→L2→Ln2→…L_0→L_n →L_1→L_{n-1}→L_2→L_{n-2}→…L0→Ln→L1→Ln1→L2→Ln2→…
要求使用原地算法,不能只改变节点内部的值,需要对实际的节点进行交换。
数据范围:链表长度 0≤n≤200000 le n le 200000≤n≤20000 ,链表中每个节点的值满足 0≤val≤10000 le val le 1000 0≤val≤1000
要求:空间复杂度 O(n)并在链表上进行操作而不新建链表,时间复杂度 O(n)
进阶:空间复杂度 O(1) , 时间复杂度 O(n)
示例1
输入:
{1,2,3,4}
返回值:
{1,4,2,3}
说明:
给定head链表1->2->3->4, 重新排列为 1->4->2->3,会取head链表里面的值打印输出
示例2
输入:
{1,2,3,4,5}
返回值:
{1,5,2,4,3}
说明:
给定head链表1->2->3->4->5, 重新排列为 1->5>2->4->3,会取head链表里面的值打印输出
示例3
输入:
{}
返回值:
{}
思路
这个题比较有意思, 把1->2->3->4->5, 变成1->5>2->4->3
我的思路是把后半段链表逆序, 1->2->3 4<-5
然后首尾指针从两边开始遍历, 修改指针:
- 头指针修改1->5, 2->3 4<-5
- 尾指针修改5->2, 1->5->2->3 4
- 头指针修改2->4 1->5->2->4 3
- 尾指针修改4->3 1->5>2->4->3
- 遍历结束
实现的难点在于边界条件的处理!
题解
边界条件的处理太容易出错了, 试错很多次, 而且目前解法好像使用内存比较多, 还有优化空间。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public void reorderList(ListNode head) {
//1. 快慢指针找到中点
//2. 反转后半段链表, 使得前半段指向中间(正序), 后半段方向也指向中间(逆序)
//3. 分别从头结点和尾结点向中间遍历, 改变指针指向
if(head == null || head.next == null || head.next.next == null){
return;
}
//找中点
ListNode fast = head;
ListNode slow = head;
while(fast.next != null && fast.next.next != null){
fast = fast.next.next;
slow = slow.next;
}
if(fast.next != null){
slow = slow.next;
}
//反转链表
ListNode next = slow.next;
slow.next = null;
ListNode nextnext;
while(next!= null && next.next != null){
nextnext = next.next;
next.next = slow;
slow = next;
next = nextnext;
}
if(next != null){
next.next = slow;
}
//next指针指向尾结点
ListNode tail = next;
ListNode headi = head;
//改变指针指向
ListNode headTem;
ListNode tailTem;
while(true){
headTem = headi.next;
headi.next = tail;
headi = headTem;
if (headi == tail || headi == null) break;
tailTem = tail.next;
tail.next = headi;
tail = tailTem;
if (headi == tail || headi == null) break;
}
}
}