【问题标题】:Find the 3rd last element in the Singly Link List - Algorithm在单链表中查找倒数第三个元素 - 算法
【发布时间】:2013-07-05 15:17:57
【问题描述】:

我正在考虑在算法上找到单链接列表中的倒数第三个元素,我自己想出了一个(空间效率低下)
使用具有 O(n) 时间复杂度 [大量空间复杂度] 的循环将链接列表放入 ArrayList
然后找到 Arraylist 的大小并在 (size-2) 索引位置检索元素[所需元素] 如果我的算法有意义,请指导我

仅供参考
我搜索的其他内容是: 放置两个指针,将第一个指针放在第一个元素上,将第二个指针放在第三个元素上,并平行移动它们
当第二个指针到达LinkList的末尾时,检索第一个指针指向的节点[所需节点]

【问题讨论】:

  • 您可以使用单个指针。遍历到ptr.next().next().next() == null;
  • @GriffeyDog 由于“遍历”是一系列.next() 调用,因此您正在执行三个遍历,而只有一个是必要的。但是,由于缓存,您的方法可能比三个单独的遍历更快。
  • @maybeWeCouldStealAVan 这取决于 OP 的目标是什么。我只是提供一个简单编码算法,它保持 O(n) 时间复杂度并且不增加空间要求。我认为OP的第二个选项会很好。
  • @GriffeyDog 够公平 :-)

标签: java algorithm linked-list


【解决方案1】:
  • 使用两个指针:pointer-1pointer-2
  • 使pointer-1指向单链表中的第三个节点。

     pointer-1 = node1->next->next; // point to 3rd nd
    
     node1-->node2-->node3-->node4---> ...... -->node-last
                     ^
                     |
                    pointer-1
    
  • 现在设置pointer-2指向第一个节点

     pointer-2 = node1;   // point to 1st nd
    
     node1-->node2-->node3-->node4---> ...... -->node-last
       ^              ^
       |              |
      pointer-2      pointer-1
    
  • 现在,在链表中循环遍历直到pointer-1 指向最后一个节点,循环中还更新指针2(每次到自身的下一个节点)

    while (pointer-1->next!=NULL){// points to last node 
         pointer-1 = pointer-1 -> next;
         pointer-2 = pointer-2 -> next;
    }
    
    When loop ends pointer-1 points to last-node, pointer-2 points to third last
    
     node1-->node2-->........ ->node-l3-->node-l2-->node-last
                                 ^                   ^
                                 |                   |
                               pointer-2            pointer-1
    

它的工作时间为 O(n),其中n 是链表中的节点数。

【讨论】:

    【解决方案2】:

    由于链表是单链的,所以需要从头到尾遍历才能知道倒数第三个元素是什么,osO(n)时间是不可避免的(除非你维护了一个指向倒数第三个元素的指针在您的链接列表中)。

    您的 2 指针想法将使用常量空间。这可能是更好的选择,因为创建 ArrayList 会产生更多开销,并占用更多空间。

    【讨论】:

      【解决方案3】:

      得到两个指针。 pA, pB 将 pA 移动到 3 个元素,将 pB 移动到头部:

      1->2->3->4-> .... ->99->100 
      |     |
      pB    pA
      

      之后,在同一个循环中移动两个指针,直到 pA 到达最后一个元素。

      此时 pB 将指向最后的第三个元素

      1->2->3->4-> .... ->98->99->100 
                          |        |
                          pB       pA
      

      编辑: 遍历将是一个:

      while(pA->next!=null){
         pA = pA->next;
         pB = pB->next;
      }
      

      这里 pB 将是倒数第三个

      【讨论】:

      • 这需要两次遍历。
      • 一次遍历,而不是两次。您将在一个 foreach 中移动两个指针
      • 这仍然是两个遍历,一起完成。最好是保存三个值,这样就不用遍历两个指针或者再次遍历就可以找到结果了。
      • “遍历”是一系列 n 个“下一个”操作。你做了将近 2n(真的是 2n-2)。
      • @maybeWeCouldStealAVan 好的。
      【解决方案4】:

      解决这个问题的另一种观点,即使它是 O(n)

      创建一个空数组(n),从0开始指向这个数组的指针,同样从链表的开头开始。每次访问一个节点时,将其存储在数组的当前索引中并推进数组指针。继续填充数组中的节点,当到达数组末尾时,请重新从头开始,以便覆盖。 现在,一旦您结束列表的最后一端,指针 nth elemtn from end :)

      【讨论】:

        【解决方案5】:

        在实践中,Java LinkedList 会跟踪其大小,因此您可以转到“list.get(list.size()-2)”,但如果使用构造的链表,我会这样做以下:

        保留一个包含 3 个值的数组。遍历列表时,添加更新 array[i%3] 的值。当没有更多值时,返回array[(i-2)%3]处的值。

        【讨论】:

          【解决方案6】:

          我要做的第一件事是要求您重新考虑使用单链表。为什么不将数据存储在 ArrayList 中?它可以完成您想要的一切,而且它是内置的,因此与大多数人编写的相比,它相对高效。

          如果您被束缚并决定使用链表,我建议您保留指向倒数第三个元素 (tle) 的直接指针。插入很容易——如果它们出现在 tle 之前,什么也不做;如果之后,将 tle 指针推进到 tle 的下一个。删除更麻烦,但可能不是大多数时候。如果删除发生在 tle 之前,它仍然是 tle。烦人的情况是,如果要删除的元素是 tle 或更高版本,在这种情况下,您可以从头开始迭代,直到当前节点的 next() 引用是 tle。当前节点将成为新文件,然后您可以继续删除。由于只有三个节点调用此级别的工作,因此如果列表相当大,您可能大部分时间都可以正常工作。

          如果经常从末尾删除,则最后一个选择是从后到前维护您的列表。然后,出于维护目的,该 tle 变为从前数第三个。

          【讨论】:

            【解决方案7】:
                //We take here two pointers pNode and qNode both initial points to head
                //pNode traverse till end of list and the qNode will only traverse when difference
               // between the count and position is grater than 0 and pthNode increments once
              // in each loop
                static ListNode nthNode(int pos){
                ListNode pNode=head;
                ListNode qNode=head;
                int count =0;
                while(qNode!=null){
                    count++;
                    if(count - pos > 0)
                        pNode=pNode.next;
                    qNode=qNode.next;
                }
                return pNode;
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-06-21
              • 1970-01-01
              • 2013-09-20
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-04-21
              • 2011-01-21
              相关资源
              最近更新 更多