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

时间:2016-11-15 18:43:50

标签: 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,我的解决方案return 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;
    }
}

1 个答案:

答案 0 :(得分:1)

不改变你的程序所做的事情, 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。 此外,postcurr领先一步。

  • currpost一起移动。 在curr条件中,postif未更改, 并作为循环的最后一步, 他们都向前推进了一个位置。

给出输入1,1,1,2,3以及上述观察结果:

  • if达到2之前,外post条件将为false。

  • curr落后一步,所以它指向2之前的那个。

  • prev没有移动,因此prev.next仍然指向第一个。

  • 所以此时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

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

这就是结束。 prev从未移动过,它停留在原来的位置,即dummyprev.next指向3,我们返回错误。 请注意,如果输入较长,例如1,1,1,2,3,4,5,6, 相同的行为将继续, prev.next之后的curr, 并且实现将错误地返回6 -> null。 算法被打破了。

考虑这种替代算法:

  1. 创建一个虚拟节点,指向下一个head(就像您已经做过的那样)
  2. 将当前节点设置为虚拟
  3. 当下一个节点存在时,下一个节点存在
    • 比较next.valnext.next.val
    • 如果不相等,则前进当前节点
    • 如果相等,那么:
      • 保存next.val
      • 的副本
      • 跳过nextnext.next(设置在next.next.next旁边)
      • 跳过其值等于保存的val
      • 的所有其他节点
  4. 返回dummy.next
  5. 像这样:

    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;