给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。(详情参见https://leetcode-cn.com/problems/partition-list/)
ADT:
public class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x; }
}
我的思路:用一条链表存放比x小得节点,再用一条存放比x大得节点,再将两条链表合并即可。
说明:思路没毛病,但在代码实现上出了问题,苦思半天终于找到bug。
bug修改前的代码:
class Solution {
public ListNode partition(ListNode head, int x) {
//声明两个链表头节点
ListNode head1 = new ListNode(0);
ListNode head2 = new ListNode(0);
//声明一个在原链表上用以移动的指针
ListNode cur = head;
//声明两个用以移动的指针
ListNode l1 = head1;
ListNode l2 = head2;
while(cur!=null){
//当前原节点的值比x小,链接在l1,否则l2
if(cur.val < x){
l1.next = cur;
l1 = l1.next;
cur = cur.next;
}else{
l2 .next = cur;
l2 = l2.next;
cur = cur.next;
}
}
//最后去除两条链表的头节点再合并两条链表
head1 = head1.next;
head2 = head2.next;
l1.next = head2;
return head1;
}
}
思路看似没问题,但是对于一下这段代码:bug1
l1.next = cur;
l1 = l1.next;
cur = cur.next;
其变化是这样(黑色为变化前,红色为变化后)
画图分析可知会导致各种指针指向混乱,导致后面指针寻址异常
bug1改进:
l1.next = cur;
l1 = l1.next;
cur = cur.next;
l1.next = null;//注意断开l1的链接要放在l1和cur移动之后
bug2:
head1 = head1.next;
head2 = head2.next;
l1.next = head2;
return head1;
这段代码看似没毛病,实际在极端情况下会出问题,比如,如果head1.next为空(当所有节点都比x大),而l1依然指向head1所在的节点,所以此时返回的值也为空。
改进:
l1.next = head2.next;//l1链上去头后的l2链表
return head1.next;//返回去头后的l1
总结:尽量不要改变不移动的指针
以下附上完整正确代码:
public ListNode partition(ListNode head, int x) {
ListNode head1 = new ListNode(0);
ListNode head2 = new ListNode(0);
ListNode cur = head;
ListNode l1 = head1;
ListNode l2 = head2;
while(cur!=null){
if(cur.val < x){
l1.next = cur;
cur = cur.next;
l1 = l1.next;
l1.next = null;
}else{
l2 .next = cur;
cur = cur.next;
l2 = l2.next;
l2.next = null;
}
}
l1.next = head2.next;
return head1.next;
}