题目
题目分析
这道题猛一眼看着很简单,但是这是链表,而不是数组。如果是数组的话,Arrays.sort()一下就行。我一开始就是这么想的,看能不能转换成List,再变成数组,,太麻烦了,一看就不是正确解法。
官方给的方法(从底向顶进行合并排序)
- 计算出来链表长度length
- 对链表进行划分,每个小链表的长度为subLength,然后进行两两合并,subLength=length。
合并的时候用到了Leetcode 21. 合并有序链表
注意对链表进行安全剪断:
ListNode head2 = curr.next; // 1. 保存当前节点的下一个节点(即第二个子链表的头节点)
curr.next = null; // 2. 断开当前节点与后续节点的连接
curr = head2; // 3. 将 curr 移动到第二个子链表的头节点
deepseek给出的解释:
这段代码的作用是 将链表从当前节点 curr
处断开,分成两个独立的子链表,并移动 curr
指针到第二个子链表的头部。具体解析如下:
代码解释
ListNode head2 = curr.next; // 1. 保存当前节点的下一个节点(即第二个子链表的头节点)
curr.next = null; // 2. 断开当前节点与后续节点的连接
curr = head2; // 3. 将 curr 移动到第二个子链表的头节点
-
ListNode head2 = curr.next
- 将
curr.next
(当前节点的下一个节点)保存到临时变量head2
中。 - 此时
head2
指向第二个子链表的头节点。
- 将
-
curr.next = null
- 将
curr.next
设为null
,断开当前节点与后续节点的连接。 - 此时第一个子链表以
curr
结尾,第二个子链表以head2
开头。
- 将
-
curr = head2
- 将
curr
指针移动到head2
(即第二个子链表的头节点),以便后续处理。
- 将
使用场景
这种操作常见于 链表的分割或归并排序 中,例如:
- 归并排序:在合并前需要将链表从中间断开,分成两个独立子链表。
- 快慢指针分割:用快慢指针找到链表中点后,断开链表为前后两部分。
示例
假设链表为 1 -> 2 -> 3 -> 4
,curr
初始指向节点 1
:
- 执行前:
curr
指向1
,curr.next
指向2
。
- 执行后:
head2
指向2
(第二个子链表的头)。1.next
被设为null
,链表被拆分为1
和2 -> 3 -> 4
。curr
移动到head2
(即节点2
)。
类比理解
可以想象为用剪刀在链表的 curr
节点处剪一刀:
- 剪之前:
[原链表]
→curr -> [后续节点]
- 剪之后:
[原链表]
→curr
(断开) +[后续节点]
(由head2
指向)
注意事项
- 边界条件:若
curr
已经是链表末尾(curr.next == null
),则head2
为null
,后续操作需判空。 - 内存安全:断开链表后,需确保所有子链表最终被正确合并或释放,避免内存泄漏。
这种操作是链表处理中的基础技巧,尤其在分治算法(如归并排序)中至关重要。
这段代码的作用是 安全地断开当前节点 cur
与后续节点的连接,并保存下一个节点的引用到 next
变量中。以下是逐步解析:
代码解释
ListNode next = null; // 1. 初始化 next 为 null(避免空指针异常)
if (cur != null) { // 2. 检查 cur 是否非空
next = cur.next; // 3. 保存 cur 的下一个节点到 next
cur.next = null; // 4. 断开 cur 与后续节点的连接
}
-
ListNode next = null
- 初始化
next
为null
,防止后续操作中出现空指针异常(如果cur
本身就是null
)。
- 初始化
-
if (cur != null)
- 检查当前节点
cur
是否为非空,避免对空节点操作导致崩溃。
- 检查当前节点
-
next = cur.next
- 将
cur
的下一个节点保存到临时变量next
中,以便后续使用(例如合并或遍历)。
- 将
-
cur.next = null
- 将
cur.next
设为null
,断开cur
与后续节点的连接。此时cur
成为独立节点。
- 将
使用场景
这种操作通常出现在以下场景:
- 链表分割(如归并排序中拆分链表)。
- 链表反转(需要断开并反转指针方向)。
- 删除节点(断开连接后重新链接或释放内存)。
示例
假设链表为 A -> B -> C
,cur
指向节点 B
:
- 执行前:
cur
指向B
,cur.next
指向C
。
- 执行后:
next
保存C
的引用。B.next
被设为null
,链表变为A -> B
和C
两段。- 此时可通过
next
继续处理剩余链表(C
)。
注意事项
- 防御性编程:
- 通过
if (cur != null)
避免空指针异常,确保代码健壮性。
- 通过
- 内存管理:
- 断开链表后,需确保后续逻辑正确处理
next
,避免内存泄漏或丢失引用。
- 断开链表后,需确保后续逻辑正确处理
- 边界条件:
- 如果
cur
是尾节点(cur.next == null
),next
会保持为null
。
- 如果
对比其他写法
- 不检查
cur
是否为空:直接next = cur.next
可能在cur
为null
时抛出异常。 - 不断开连接:若忘记
cur.next = null
,可能导致链表结构混乱(如合并排序中未断开会引发无限循环)。
总结
这段代码是链表操作中的 “安全断开” 模式,核心目的是:
- 保存后续节点引用(
next
)。 - 隔离当前节点(
cur.next = null
)。 - 安全处理边界条件(
cur
为null
的情况)。
在算法中(如归并排序、反转链表),这种操作是拆分或重组链表的关键步骤。
全部代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode sortList(ListNode head) {
// ListNode dummy = new ListNode(0,head);
// while(head!=null&&head.next!=null){
// Node one = head;
// Node two = head.next;
// if(two<one){
// two.next=one;
// dummy.next=two;
// }
// head=head.next;
// }
if(head==null){
return head;
}
int length = 0;
ListNode node = head;
while(node!=null){
length++;
node=node.next;
}
ListNode dummy = new ListNode(0,head);
//将sublength翻倍
for(int subLength = 1; subLength<length ; subLength <<=1 ){
ListNode pre = dummy, cur = dummy.next;
while(cur!=null){
ListNode head1 = cur;
for(int i =1; i<subLength && cur!=null&& cur.next!=null ; i++){
cur = cur.next;
}
ListNode head2 = cur.next;//保存cur.next作为head2
cur.next = null;//断开当前节点与后续节点的连接。此时,第一个链表以cur结尾,第二个链表以head2开头
cur = head2;//当前指针指向Head2
for(int i = 1; i<subLength && cur!=null&& cur.next!=null;i++){
cur=cur.next;
}
ListNode next = null;
if(cur!=null){
next = cur.next; //保存后续节点
cur.next=null; //将当前节点与后续节点断开
}
//合并两个链表
ListNode merged = merged(head1,head2);
//将合并的链表挂在pre节点的后面
pre.next=merged;
//将pre节点移动到合并链表的末尾
while(pre.next!=null){
pre=pre.next;
}
//当前指针指向下一段链表的头节点
cur = next;
}
}
return dummy.next;
}
public ListNode merged (ListNode head1, ListNode head2){
if(head1==null){
return head2;
}else if(head2==null){
return head1;
}else if(head1.val<head2.val){
head1.next = merged(head1.next,head2);
return head1;
}else {
head2.next = merged(head1,head2.next);
return head2;
}
}
}