1 题目链接
2 题目要求
描述
将给定的单链表 L: L0→L1→…→Ln-1→Ln
重新排序为:L0→Ln →L1→Ln-1→L2→Ln-2→…
要求使用原地算法,不能只改变节点内部的值,需要对实际的节点进行交换。
例如:
对于给定的单链表{10,20,30,40},将其重新排序为{10,40,20,30}。
3 代码思路
单链表查找只能顺着链表方向,读完题目之后,我们发现偶数结点的n、n-1这部分是逆序的,为了挨个遍历这部分并插入,我们需要找到这些结点,并将其翻转。
从哪里开始翻转呢,奇数和偶数各占一半,因此我们只需要找到中间结点即可。当然,总数如果是奇数,找到是最中间的结点,为偶数,找到后一半的第一个结点。怎么找?用双指针法,前一个指针每次走一步,后一个指针每次走两步,后一个指针走到最后,第一个指针刚好找到中间结点。
随后,我们将前一半顺序的结点和后一半逆序的结点合并成一个链表即可。
4 代码实现
public class Solution {
public void reorderList(ListNode head) {
if(head == null || head.next == null)
return;
ListNode midNode = getMid(head);
ListNode head1 = reverse(midNode);
merge(head,head1);
}
// 获取中间结点
public ListNode getMid(ListNode head){
if(head == null)
return head;
// 双指针法,p为前一个指针,q为后一个指针。
// pre指针是为了找到中间结点的前一个结点,随后将其next置空,达到前一半和后一半分离的目的。
// 不使用pre也可以实现,只不过这种我认为可读性更好一些
ListNode pre = null;
ListNode p = head;
ListNode q = head;
while(q != null && q.next != null){
pre = p;
p = p.next;
q = q.next.next;
}
pre.next = null;
return p;
}
// 翻转链表
public ListNode reverse(ListNode head){
if(head == null)
return head;
// 三个指针原地翻转链表
ListNode pre = null;
ListNode cur = head;
ListNode post = cur.next;
while(cur != null){
cur.next = pre;
pre = cur;
cur = post;
if(post != null)
post = post.next;
}
return pre;
}
// 合并链表
public ListNode merge(ListNode head, ListNode head1){
ListNode p = head;
ListNode cur = head1;
while(cur != null){
ListNode next = cur.next;
cur.next = p.next;
p.next = cur;
cur = next;
// 总数是奇数或者偶数,最后一个结点插入方法不同。
if(p.next.next != null)
p = p.next.next;
else
p = p.next;
}
return head;
}
}
5 知识补充
比较想知道对于以上这种总结方式,是否有不足的地方,有没有要改进的地方。比如代码思路这里,用不用写的更加详细,写出具体链表操作步骤图。欢迎各位大佬提意见。