携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第21天,点击查看活动详情
前言
周四了,周五不远拉!!坚持就是胜利!
[1014. 重排链表] 难度:中等,今天的题简直太经典了,我为了做这题,提前预热了三天。
题目
给定一个单链表 L 的头节点 head ,单链表 L 表示为:
L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
输入: head = [1,2,3,4]
输出: [1,4,2,3]
示例 2:
输入: head = [1,2,3,4,5]
输出: [1,5,2,4,3]
审题
非常经典的链表题目,使用第二种方法可以学习翻转链表、合并链表、快慢指针。
讲之前先提一下本题解决思路很多,第一种方法为使用数组来记录长度然后长度对半分获取到中点,由中点来来决定前后链表,将后链表拆除并倒序排列,然后在依次插入到前链表当中完成结果。
第一种方法使用到了空间开辟比较大,并且也是最简单的一种,但是效率最高是使用第二种方法,第二种方法有三个步骤 1. 寻找链表中点 2. 对后链表进行翻转 3. 合并两个链表,可以发现这三个逻辑与上面第一种是一样的只不过上面使用的方法是用数组实现前两者。
知道了三个步骤我们来依次攻破,第一步寻找链表中点,我这里使用的是快慢指针来找出链表中点,然后第二步是对后链表翻转,链表翻转,翻转链表与冒泡中的交换法如出一辙。第三步还多执行了一个deleteTail方法,这是为了删掉head链表中冗余的尾部节点,然后在合并链表,完成题目。
我的解题代码
class Solution {
public ListNode reorderList(ListNode head) {
//1 2 3 4 5
//5 4 插入 123
if (head.next == null){
return head;
}
//找链表中点
ListNode midpoint = midpoint(head);
//翻转链表
ListNode l2 = flipLinkedList(midpoint);
//第一个链表删除最后一个节点
deleteTail(head);
//合并链表
return mergeLinkedList(head, l2);
}
ListNode mergeLinkedList(ListNode l1, ListNode l2){
if (l1 == null || l2 == null) {
return l1 == null ? l2 : l1;
}
ListNode result = new ListNode(), temp = result;
while (l1 != null && l2 != null) {
temp.next = l1;
l1 = l1.next;
temp = temp.next.next = l2;
l2 = l2.next;
}
l1 = l1 == null ? l2 : l1;
if (l1 != null) {
temp.next = l1;
}
return result.next;
}
void deleteTail(ListNode head) {
if (head == null || head.next == null) {
return;
}
while (head.next.next != null){
head = head.next;
}
head.next = null;
}
ListNode midpoint(ListNode head){
ListNode fast = head;
ListNode slow = head;
while (fast.next != null && fast.next.next != null){
slow = slow.next;
fast = fast.next.next;
}
if (fast.next != null){
return slow.next;
}
return slow;
}
ListNode flipLinkedList(ListNode node){
ListNode pre = null;
ListNode cur = node;
while (cur != null){
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}