C# 面试问题:从未排序的链接列表中删除重复项

C# 面试问题:从未排序的链接列表中删除重复项,c#,java,linked-list,C#,Java,Linked List,我正在阅读并试图解决以下问题: 2.1编写代码以从未排序的链表中删除重复项。跟随 UP:如果 不允许使用临时缓冲区 我正在用C#解决它,所以我创建了自己的节点类: public class Node<T> where T : class { public Node<T> Next { get; set; } public T Value { get; set; } public Node(T value) { Next =

我正在阅读并试图解决以下问题:

2.1编写代码以从未排序的链表中删除重复项。跟随 UP:如果 不允许使用临时缓冲区

我正在用C#解决它,所以我创建了自己的
节点
类:

public class Node<T> where T : class
{
    public Node<T> Next { get; set; }
    public T Value { get; set; }

    public Node(T value)
    {
        Next = null;
        Value = value;
    }
}

因此,我的解决方案始终从当前节点到最后查找重复项,而他们的解决方案从头部到当前节点查找重复项。我觉得这两种解决方案都会遇到性能问题,这取决于列表中有多少重复项以及它们的分布方式(密度和位置)。但总的来说:我的答案几乎和书中的一样好还是更糟?

你的解决方案和作者的一样好,只是它在实现中有一个缺陷:)试着在两个数据相同的节点列表上跟踪它。

你的方法与书中的方法完全一致!你向前走,书就向后走。没有区别,因为你们两个都扫描所有元素。而且,是的,由于不允许使用缓冲区,因此存在性能问题。在没有明确要求的情况下,您通常不必在意此类成本训练问题的性能

面试问题是为了测试你的开放性。我对Mark的答案表示怀疑:它肯定是现实世界示例中的最佳解决方案,但即使这些算法使用常量空间,也必须遵守不允许使用临时缓冲区的限制

否则,我猜这本书会采用这样的方法。马克,请原谅我批评你


无论如何,更深入地说,你和这本书的方法都需要
Theta(n^2)
时间,而马克的方法需要
Theta(n logn)+Theta(n)
时间,这导致
Theta(n logn)
。为什么
Theta
?因为比较交换算法也是
Omega(nlogn)
,记住

差别不大。如果我算对了,你的平均速度比作者慢N/16,但在很多情况下,你的实现会更快

编辑:

我称你的实现为Y,而作者是A

当所有元素的值相同时,两个建议的解决方案的最坏情况都是O(N^2),并且它们的最佳情况都是O(N)

编辑: 这完全是重写。受评论中争论的启发,我试图找到随机N个随机数的平均情况。这是一个具有随机大小和随机分布的序列。平均情况是什么

Y将始终运行U次,其中U是唯一数字的数目。对于每个迭代,它将进行N-X比较,其中X是迭代之前移除的元素数(+1)。第一次没有删除任何元素,第二次迭代平均删除N/U

也就是说,平均½N将留待迭代。我们可以将平均成本表示为 U*½N。平均U也可以基于N表示为0 表达情感变得更加困难。假设我们在遇到所有唯一值之前使用I迭代。之后,将在1和U之间进行比较(平均为U/”),并将进行N-I次比较

I*c+U/2(N-I)

但是,我们在第一次迭代中运行的平均比较次数(c)是多少?平均而言,我们需要与已经访问的元素的一半进行比较,平均而言,我们访问了I/2元素,即c=I/4

I/4+U/2(N-I)

我可以用N来表示。平均来说,我们需要访问N的一半才能找到唯一的值,因此I=N/2,得到的平均值为

(I^2)/4+U/2(N-I),可简化为(3*N^2)/16


当然,如果我对平均值的估计是正确的话。这是任何潜在序列的平均值,A的比较比Y少N/16,但Y比A快的情况有很多。所以我想说,与比较次数相比,它们是相等的。如果你给一个人一条鱼,他们吃一天。如果你教一个人钓鱼

我对实施质量的衡量标准是:

  • 正确性:如果你没有在所有情况下都得到正确的答案,那么它还没有准备好
  • 可读性/可维护性:查看代码重复、可理解的名称、每个块/方法的代码行数(以及每个块所做的事情的数量),以及跟踪代码流有多困难。如果您想了解更多信息,请阅读大量关于重构、编程最佳实践、编码标准等方面的书籍
  • 理论性能(最坏情况和弹药化):是一个可以使用的指标。CPU和内存消耗都应该测量
  • 复杂度:估计一个普通的专业程序员如何实现(如果他们已经知道算法的话)。看看这是否与问题的实际难度相符
至于你的实施:

  • 正确性:我建议您自己编写单元测试来确定这一点,并/或(在纸上)使用有趣的示例/边缘案例从头到尾调试它。Null、一项、两项、不同数量的重复项,等等
  • 可读性/可维护性:虽然您的最后两条注释没有添加任何内容,但它看起来基本不错。您的代码所做的事情比本书中的代码更为明显
  • 性能:我相信两者都是N平方。无论其中一个的摊余成本是更低的,我都会让你算出:)
  • 实施时间:一个普通的专业人士应该能够在睡眠中编写这个算法,所以看起来不错

    • 使用HashMap怎么样?这样会占用O(n)个时间和O(n)个空间。我将编写伪代码

      function removeDup(LinkedList list){
        HashMap map = new HashMap();
        for(i=0; i<list.length;i++)
            if list.get(i) not in map
              map.add(list.get(i))
            else
              list.remove(i)
            end
        end
      end
      
      函数删除
      
      public static void deleteDups2(LinkedListNode head) 
      {
          if (head == null) return;
      
          LinkedListNode previous = head;
          LinkedListNode current = previous.next;
      
          while (current != null) 
          {
              LinkedListNode runner = head;
      
              while (runner != current) { // Check for earlier dups
                  if (runner.data == current.data) 
                  {
                      LinkedListNode tmp = current.next; // remove current
                      previous.next = tmp;
                      current = tmp; // update current to next node
                      break; // all other dups have already been removed
                  }
                  runner = runner.next;
              }
              if (runner == current) { // current not updated - update now
                  previous = current;
                  current = current.next;
              }
          }
      }
      
      function removeDup(LinkedList list){
        HashMap map = new HashMap();
        for(i=0; i<list.length;i++)
            if list.get(i) not in map
              map.add(list.get(i))
            else
              list.remove(i)
            end
        end
      end
      
      function siftUp(a, start, end) is
       input:  start represents the limit of how far up the heap to sift.
                     end is the node to sift up.
       child := end 
       while child > start
           parent := floor((child - 1) ÷ 2)
           if a[parent] < a[child] then (out of max-heap order)
               swap(a[parent], a[child])
               child := parent (repeat to continue sifting up the parent now)
           else if a[parent] == a[child] then
               remove a[parent]
           else
               return
      
      public static void dedup(Node head) {
          Node cur = null;
          HashSet encountered = new HashSet();
      
          while (head != null) {
              encountered.add(head.data);
              cur = head;
              while (cur.next != null) {
                  if (encountered.contains(cur.next.data)) {
                      cur.next = cur.next.next;
                  } else {
                      break;
                  }
              }
              head = cur.next;
          }
      }
      
      #include "stdafx.h"
      #include <stdlib.h>
      struct node
      {
          int data;
          struct node *next;
      };
      struct node *head = (node*)malloc(sizeof(node));
      struct node *tail = (node*)malloc(sizeof(node));
      
      struct node* createNode(int data)
      {
          struct node *newNode = (node*)malloc(sizeof(node));
          newNode->data = data;
          newNode->next = NULL;
          head = newNode;
          return newNode;
      }
      
      bool insertAfter(node * list, int data)
      {
          //case 1 - insert after head
          struct node *newNode = (node*)malloc(sizeof(node));
          if (!list)
          {
      
              newNode->data = data;
              newNode->next = head;
              head = newNode;
              return true;
          }
      
          struct node * curpos = (node *)malloc(sizeof(node));
          curpos = head;
          //case 2- middle, tail of list
          while (curpos)
          {
              if (curpos == list)
              {
                  newNode->data = data;
                  if (curpos->next == NULL)
                  {
                  newNode->next = NULL;
                  tail = newNode;
                  }
                  else
                  {
                      newNode->next = curpos->next;
                  }
                  curpos->next = newNode;
                  return true;
              }
              curpos = curpos->next;
          }
      }
      
      void deleteNode(node *runner, node * curr){
      
          //DELETE AT TAIL
          if (runner->next->next == NULL)
          {
              runner->next = NULL;        
          }
          else//delete at middle
          {
              runner = runner->next->next;
              curr->next = runner;
          }
          }
      
      
      void removedups(node * list)
      {
          struct node * curr = (node*)malloc(sizeof(node));
          struct node * runner = (node*)malloc(sizeof(node));
          curr = head;
          runner = curr;
          while (curr != NULL){
              runner = curr;
              while (runner->next != NULL){
                  if (curr->data == runner->next->data){
                      deleteNode(runner, curr);
                  }
                  if (runner->next!=NULL)
                  runner = runner->next;
              }
              curr = curr->next;
          }
      }
      int _tmain(int argc, _TCHAR* argv[])
      {
          struct node * list = (node*) malloc(sizeof(node));
          list = createNode(1);
          insertAfter(list,2);
          insertAfter(list, 2);
          insertAfter(list, 3);   
          removedups(list);
          return 0;
      }
      
          void removeduplicates(N **r)
          {
              N *temp1=*r;
              N *temp2=NULL;
              N *temp3=NULL;
              while(temp1->next!=NULL)
              {
                  temp2=temp1;
                  while(temp2!=NULL)
                  {
                      temp3=temp2;
                      temp2=temp2->next;
                      if(temp2==NULL)
                      {
                          break;
                      }
                      if((temp2->data)==(temp1->data))
                      {
                          temp3->next=temp2->next;
                          free(temp2);
                          temp2=temp3;
                          printf("\na dup deleted");
                      }
                  }
                  temp1=temp1->next;
              }
      
          }
      
          void removeduplicates(N **r)
          {
              N *temp1=*r;
              N *temp2=NULL;
              N *temp3=NULL;
              while(temp1->next!=NULL)
              {
                  temp2=temp1;
                  while(temp2!=NULL)
                  {
                      temp3=temp2;
                      temp2=temp2->next;
                      if(temp2==NULL)
                      {
                          break;
                      }
                      if((temp2->data)==(temp1->data))
                      {
                          temp3->next=temp2->next;
                          free(temp2);
                          temp2=temp3;
                          printf("\na dup deleted");
                      }
                  }
                  temp1=temp1->next;
              }
      
          }
      
       public Node removeDuplicates(Node head) 
          {
              if (head == null)
                  return head;
      
              var current = head;
              while (current != null)
              {
                  if (current.next != null && current.data == current.next.data)
                  {
                      current.next = current.next.next;
                  }
                  else { current = current.next; }
              }
      
              return head;
          }