【问题标题】:Linked List - remove duplicates algorithm in C#/Java链表 - C#/Java 中的删除重复算法
【发布时间】:2021-04-17 06:11:55
【问题描述】:

我正在学习 C#/Java 中的数据结构和算法。遇到Linked List去重问题的解决方法后,一直在努力理解。

解决方案是名著Cracking the coding Interview(第5版,第208页)提出的解决方案。

void RemoveDuplicates_HashSet(Node n)
{
    HashSet<object> set = new HashSet<object>();

    Node previous = null;
    while (n != null)
    {
        if (set.Contains(n.Data))       // Condition 1
            previous.Next = n.Next;
        else                            // Condition 2
        {
            set.Add(n.Data);
            previous = n;
        }
            
        n = n.Next;
    }
}

使用以下链表A-&gt;B-&gt;A-&gt;B运行代码:

// Creating test Singly LinkedList
Node n = new Node("A");
n.Next = new Node("B");
n.Next.Next = new Node("A");
n.Next.Next.Next = new Node("B");

RemoveDuplicates_HashSet(n);

工作得很好:方法后n的值是A-&gt;B

通过使用调试器跟踪代码,我可以看到方法循环中发生的情况如下:

| Pass | HashSet | n          | previous   | Comment                  |
| ---- | ------- | ---------- | ---------- | ------------------------ |
| –    | –       | A->B->A->B | null       |                          |
| 1    | A       | B->A->B    | A->B->A->B | Condition 2 is triggered |
| 2    | A,B     | A->B       | B->A->B    | Condition 2 is triggered |
| 3    | A,B     | B          | B->B       | Condition 1 is triggered |
| 4    | A,B     | null       | B          | Condition 1 is triggered |

我无法理解这实际上是如何导致的:

  1. n 删除的重复项在哪里/如何准确删除?我知道 HashSet 只包含唯一的元素,因此它会检测是否已经遇到了一个元素,但是我仍然看不到该算法的整体工作原理。

  2. n指向的值怎么更新为A-&gt;B?它在哪里,鉴于本质上循环只是在链接列表上迭代执行n = n.Nextn 实际上是用最终值A-&gt;B 更新的?我知道列表是通过引用传递的,但我看不到它实际上是如何修改的。

【问题讨论】:

  • 此代码:if (set.Contains(n.Data)) previous.Next = n.Next 检查是否已遇到该元素,如果已遇到,则从链接列表中删除 n。它通过将n.Next 分配给previous.Next 来删除节点(这意味着previous.Next 不再指向n)。
  • @Slaw 谢谢,这正是我正在寻找的答案。你让我意识到,实际上,previousn 指向同一个列表(我错误地认为它们是同一个列表的“副本”)。因此,修改previous意味着也要修改原来的列表n

标签: java c# algorithm linked-list pass-by-reference


【解决方案1】:

在哪里/如何准确地从 n 中删除重复项?

Set 的一个属性,如果它不能包含重复的元素。

if (set.Contains(n.Data))
            previous.Next = n.Next;
else
{
            set.Add(n.Data);
            previous = n;
}
n = n.Next;

这里首先检查当前节点n的数据是否包含在集合中。如果是,则前一个节点的后继节点是当前节点n的下一个节点。 所以:

[previous]->[n]->[next]
[previous]->[next]

否则,数据将添加到集合中,然后继续下一个节点。

n 指向的值怎么更新为 A->B?考虑到循环本质上是简单地在链表上迭代 n = n。接下来,n 实际上是用最终值 A->B 更新的?

我真的不明白这个问题。您的意思是“为什么要修改列表?” 如果是,那么因为它是通过引用传递的(有点 simplified )。所以你只是不做复制,就像原始类型(int,long,...)一样,但你修改了对象本身。

【讨论】:

  • > Do you mean "Why is the list modified?" 是的,我确实在一定程度上理解 pass-by-reference,但显然我仍然遗漏了一些东西——这里哪里是 n 首先被修改了?
  • 与列表本身相同。您更改节点的某些字段的值,这将显示在列表中。
【解决方案2】:

@Slaw's comment 指出了我认为正确的方向。

此代码:if (set.Contains(n.Data)) previous.Next = n.Next 检查是否已遇到该元素,如果已遇到,则从链表中删除 n。 它通过将n.Next分配给previous.Next(这意味着previous.Next不再指向n)来删除节点。

因此,我试图详尽地描绘算法中发生的事情。

【讨论】:

    猜你喜欢
    • 2014-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多