【问题标题】:How to find the middle element in linked list [closed]如何在链表中找到中间元素[关闭]
【发布时间】:2017-10-27 13:36:04
【问题描述】:

我需要一个详细的算法,在 c# 中关于如何在链表中找到中间元素。 我检查了谷歌,所有人都在谈论在列表上平行移动的两个指针。 但实际上,我找不到该算法的详细解决方案。以及这两个指针应该如何实现。

我需要关于性能的最佳解决方案。

【问题讨论】:

  • 在一个双链表中,从头到尾工作直到他们相遇。在单个链表中,只需处理一个变量,该变量一次在列表中移动一项,而第二个变量一次移动两项。当第二个到达末尾时,第一个应该在中间。
  • 当列表中有偶数个元素时,您将什么定义为“中间元素”(例如,如果有六个元素,那么它是第三个元素 - 它前面有两个,后面有 3 个 -或第四个有 3 个前面和 2 个后面的元素)?
  • 这个问题真的很蠢。在双链表或单链表中,您只需添加 int Count 属性,然后从头部进行 Count/2 迭代。在面试中问这样的问题的人通常也很愚蠢。按照我建议的方式执行此操作可为您提供 O(N/2) 性能,并且与双向链表的 2 迭代器方法相比,您的 CPU 周期将减少两倍。

标签: c# .net oop data-structures


【解决方案1】:

这几乎就是juharr 在单链表的评论中建议您的内容。

GetMiddle 从列表的头部开始,rightPointer 向前看两个元素,leftPointer 看下一个元素(两个指针向同一方向移动)。最后,当没有更多元素要检查时,leftPointer 是列表的中间节点。

在下面的代码中Node 是一个单链表节点,List 只是将元素添加到列表中并暴露其head

public T GetMiddle<T>(List<T> list)
{
    Node<T> leftPointer = list.Head;
    Node<T> rightPointer = list.Head;

    while (rightPointer != null && rightPointer.Next != null)
    {
        rightPointer = rightPointer.Next.Next;
        leftPointer = leftPointer.Next;
    }

    return leftPointer.Item;
}

public class List<T>
{
    public Node<T> Head { get; private set; }
    private Node<T> Last;

    public void Add(T value)
    {
        Node<T> oldLast = Last;
        Last = new Node<T>(value);

        if (Head == null)
        {
            Head = Last;
        }
        else
        {
            oldLast.Next = Last;
        }
    }
}

public class Node<T>
{
    public T Item { get; private set; }
    public Node<T> Next { get; set; }

    public Node(T item)
    {
        Item = item;
    }
}

如果是偶数个元素,比如[1, 9]

var list = new List<int>();
foreach (var number in  Enumerable.Range(1, 9))
{
    list.Add(number);
}

Console.WriteLine(GetMiddle(list));

中间元素是5

但是,如果元素数量为偶数,[1, 10] 行,算法将产生6。这是因为当right 位于9 时,下一个不是null 而是10。所以当我们完成这个迭代时,right 指向nullleft 指向6(我们返回中间)。

right: 1 -> 3 -> 5 -> 7 -> 9 -> null | end
left:  1 -> 2 -> 3 -> 4 -> 5 -> 6    | end

这意味着即使在这种情况下,您也需要决定将哪个元素作为中间元素 - 5 或 6。如果您想要 5,则需要在循环中添加一个额外的条件:

rightPointer = rightPointer.Next.Next;        
if (rightPointer != null)
{
    leftPointer = leftPointer.Next;
}

【讨论】:

    【解决方案2】:

    找到与性能相关的中间元素的最佳解决方案是计算它:

    var mid = list[list.Length/2];
    

    list.Length 为偶数时返回中间之后的元素。

    如果想在list.Length为偶数时返回中间之前的元素,可以减少截断:

    var mid = list[(int)((list.Length-0.5)/2)];
    

    【讨论】:

      【解决方案3】:

      上述情况的 Java 解决方案将是:

      public ListNode middleNode(ListNode head) {
          if(head == null || head.next == null) { return head; }
          ListNode slow = head;
          ListNode fast = head;
      
          while(fast != null && fast.next != null) {
              slow = slow.next;
              fast = fast.next.next;
          }
      
          return slow;
      }
      

      这里的节点是:

      public class ListNode {
          int val;
          ListNode next;
          ListNode(int x) { val = x; }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-17
        • 2021-04-03
        • 2018-11-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多