Java 从已排序的链表中删除重复的节点。为什么我的输出错误?

Java 从已排序的链表中删除重复的节点。为什么我的输出错误?,java,algorithm,linked-list,Java,Algorithm,Linked List,给定已排序的链表,删除具有重复编号的所有节点,只保留与原始列表不同的编号 示例: 给定1->2->3->3->4->4->5->null,返回1->2->5->null 给定1->1->1->2->3->null,返回2->3->null 问题: 给定1->1->1->2->3->null,我下面的解决方案返回3->null 有人能告诉我为什么吗 /** * Definition for ListNode * public class ListNode { * int va

给定已排序的链表,删除具有重复编号的所有节点,只保留与原始列表不同的编号

示例:

  • 给定
    1->2->3->3->4->4->5->null
    ,返回
    1->2->5->null

  • 给定
    1->1->1->2->3->null
    ,返回
    2->3->null

问题:

  • 给定
    1->1->1->2->3->null
    ,我下面的解决方案
    返回3->null
有人能告诉我为什么吗

/**
 * Definition for ListNode
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    /**
     * @param ListNode head is the head of the linked list
     * @return: ListNode head of the linked list
     */
    public static ListNode deleteDuplicates(ListNode head) {
        // write your code here
        if(head == null || head.next == null) return head;

        ListNode post = head.next;
        ListNode curr = head;
        ListNode dummy = new ListNode(head.val-1);  // make sure dummy node value is different from the head
        dummy.next = head;
        ListNode prev = dummy;

        while(post != null) {
            //System.out.println("prev.val = " + prev.val + ", curr.val = " + curr.val + ", post.val = " + post.val + ", prev.next.val = " + prev.next.val);   
            //System.out.println("prev.next.val = " + prev.next.val + ", curr.next.val = " + curr.next.val);            

            if(prev.next.val == curr.val && prev.next.val == post.val) {
                curr = curr.next;
                post = post.next;
            }
            else if(prev.next.val == curr.val && prev.next.val != post.val) {
                curr = curr.next;
                post = post.next;                
                prev.next = curr;
            }
            else {
                prev = prev.next;
                curr = curr.next;
                post = post.next;
            }
        }

        return dummy.next;
    }
}

在不改变程序功能的情况下,
while
循环可以转换为更可读的形式:

while (post != null) {
    if (prev.next.val != curr.val || prev.next.val != post.val) {
        if (prev.next.val == curr.val) {
            prev.next = curr.next;
        } else {
            prev = prev.next;
        }
    }
    curr = curr.next;
    post = post.next;
}
这相当于您的实际代码。 我会根据这个版本来解释,, 因为我觉得这更容易阅读和推理

让我们观察几件事:

  • 开始时,
    prev.next
    指向
    curr
    。 所以
    prev.next.val
    等于
    curr.val
    。 而且,
    post
    curr
    领先一步

  • curr
    post
    一起移动。
    curr
    post
    if
    条件内不会更改, 作为循环的最后一步, 他们都向前推进一个位置

考虑到输入1、1、1、2、3和上述观察结果:

  • 外部
    if
    条件将为false,直到
    post
    达到2

  • curr
    落后一步,因此它指向2之前的1

  • prev
    没有移动,因此
    prev.next
    仍然指向前1

  • 因此在这一点上,
    prev.next.val
    等于
    curr.val
    (两者均为1), 但是它不等于post.val,也就是2。 因此,我们进入外部if

  • 作为
    prev.next.val==curr.val
    ,我们还输入了内部
    if
    , 并设置
    prev.next=curr.next
    。 请记住,循环的最后一步是将
    curr
    前进到
    curr.next
    。 所以
    prev.next
    将指向
    curr

  • 在下一次迭代中,我们在第3处设置了
    post
    ,在第2处设置了
    curr
    ,在
    prev.next
    处设置了
    curr
    。因此,我们输入另一个
    if
    ,然后输入内部
    if
    ,将
    prev.next
    设置为
    curr.next
    ,即3

这就是结局
prev
从未移动,它停留在原来的位置,这是
虚拟的
prev.next
指向3,返回的值不正确。 请注意,如果输入较长,例如1、1、1、2、3、4、5、6, 同样的行为还会继续,
prev.next
后面的
curr
, 并且实现将错误地返回
6->null
。 算法被破坏了


考虑以下替代算法:

  • 创建一个虚拟节点,指向
    head
    作为下一步(正如您已经做的那样)
  • 将当前节点设置为dummy
  • 而下一个节点存在,下一个节点存在
    • 比较
      next.val
      next.next.val
    • 如果不相等,则前进当前节点
    • 如果相等,则:
      • 保存一份
        next.val的副本
      • 跳过
        next
        next.next
        (设置为
        next.next.next
      • 跳过值等于保存的
        val
  • 返回
    dummy.next
  • 像这样:

    if (head == null) return head;
    
    ListNode dummy = new ListNode(head.val - 1);
    dummy.next = head;
    
    ListNode node = dummy;
    while (node.next != null && node.next.next != null) {
        if (node.next.val != node.next.next.val) {
            node = node.next;
        } else {
            int val = node.next.val;
            node.next = node.next.next.next;
            while (node.next != null && node.next.val == val) {
                node.next = node.next.next;
            }
        }
    }
    
    return dummy.next;
    

    尝试返回head@NicolasFilotto,该类在上的注释中top@aimee,返回0->1->1->2->3->Null如果要找出您的工作不正常的原因,最好的方法是在调试器中单步执行。这样,您可以检查
    prev
    curr
    post
    变量,并查看采用了哪些分支。或者,你可以假装是一台计算机,用一个简短的样本列表一步一步地遍历算法。(在源代码级调试器出现之前,我们称之为“桌面检查”)@JimMischel,我的问题是,我已经在纸上和笔上写过多次了,我认为这是一种我遗漏了一些琐碎的东西,但我不知道是什么的场景!调试器很棒,但我也在练习在没有调试器的情况下调试问题,并提高我的白板无bug技能。@JimMischel你说得对,我误读了说明。我现在修好了。我同意你的算法是有效的。但为什么我的算法和/或实现不能正常工作?这就是我最初的问题。@elixir我在顶部添加了更多的解释,详细说明了您的算法是如何崩溃的,然后介绍了另一种可行的算法。