【问题标题】:Java Using Nodes with LinkedListJava 使用带有 LinkedList 的节点
【发布时间】:2015-02-26 04:11:47
【问题描述】:

我一直在研究我最近买的一本书中的一些标准编码面试问题,我遇到了以下问题和答案:

实现一个算法来查找链表中倒数第 n 个元素。

这里是提供的答案:

public static LinkedListNode findNtoLast(LinkedListNode head, int n) { //changing LinkedListNode to ListNode<String>
        if(head == null || n < 1) {
            return null;
        }
        LinkedListNode p1 = head;
        LinkedListNode p2 = head;
        for(int j = 0; j < n-1; ++j) {
            if(p2 == null) {
                return null;
            }
            p2 = p2.next;
        }
        if(p2 == null) {
            return null;
        }
        while(p2.next != null) {
            p1 = p1.next;
            p2 = p2.next;
        }
        return p1;
    }

我了解算法、它是如何工作的,以及为什么这本书将其列为答案,但我对如何访问 LinkedListNodes 以作为参数发送给该方法感到困惑。我知道我必须创建一个 LinkedListNode 类(因为 Java 还没有),但我似乎无法弄清楚如何做到这一点。这很令人沮丧,因为我觉得我应该知道该怎么做。这是我一直在做的事情。我将不胜感激任何澄清。您可以扩展/评论我的代码或提供您自己的替代方案。谢谢。

class ListNode<E> {
    ListNode<E> next;
    E data;

    public ListNode(E value) {
        data = value;
        next = null;
    }

    public ListNode(E value, ListNode<E> n) {
        data = value;
        next = n;
    }

    public void setNext(ListNode<E> n) {
        next = n;
    }
}

public class MyLinkedList<E> extends LinkedList {
    LinkedList<ListNode<E>> list;
    ListNode<E> head;
    ListNode<E> tail;
    ListNode<E> current;
    ListNode<E> prev;

    public MyLinkedList() {
        list = null;
        head = null;
        tail = null;
        current = null;
        prev = null;
    }

    public MyLinkedList(LinkedList<E> paramList) {
        list = (LinkedList<ListNode<E>>) paramList; //or maybe create a loop assigning each ListNode a value and next ptr
        head = list.getFirst();
        tail = list.getLast(); //will need to update tail every time add new node
        current = null;
        prev = null;
    }

    public void addNode(E value) {
        super.add(value);
        //ListNode<E> temp = tail;
        current = new ListNode<E>(value);
        tail.setNext(current);
        tail = current;
    }

    public LinkedList<ListNode<E>> getList() {
        return list;
    }

    public ListNode<E> getHead() {
        return head;
    }

    public ListNode<E> getTail() {
        return tail;
    }

    public ListNode<E> getCurrent() {
        return current;
    }

    public ListNode<E> getPrev() {
        return prev;
    }


}

LinkedListNode 怎样才能从 LinkedList 中获取?

更新:我认为我的部分困惑来自于在 main 方法中放入什么。我需要创建 ListNode 的 LinkedList 吗?如果我这样做,我将如何将 ListNodes 相互连接?在不使用 LinkedList 集合对象的情况下如何连接它们?如果有人可以向我展示他们将如何编写 main 方法,我认为这将使我有足够的视角来解决我的问题。这是我对 main 方法的最新尝试:

public static void main(String args[]) {
        LinkedList<ListNode<String>> list = new LinkedList<ListNode<String>>();
        //MyLinkedList<ListNode<String>> list = new MyLinkedList(linkedList);
        list.add(new ListNode<String>("Jeff"));
        list.add(new ListNode<String>("Brian"));
        list.add(new ListNode<String>("Negin"));
        list.add(new ListNode<String>("Alex"));
        list.add(new ListNode<String>("Alaina"));
        int n = 3;
        //ListIterator<String> itr1 = list.listIterator();
        //ListIterator<String> itr2 = list.listIterator();
        LinkedListNode<String> head = new LinkedListNode(list.getFirst(), null);
        //String result = findNtoLast(itr1, itr2, n);
        //System.out.println("The " + n + "th to the last value: " + result);
        //LinkedListNode<String> nth = findNtoLast(list.getFirst(), n);
        ListNode<String> nth = findNtoLast(list.getFirst(), n);
        System.out.println("The " + n + "th to the last value: " + nth);
    }

为了尝试在不使用自定义链表类的情况下连接节点,我将 ListNode 类编辑为以下内容:

class ListNode<E> {
    ListNode<E> next;
    ListNode<E> prev; //only used for linking nodes in singly linked list
    ListNode<E> current; //also only used for linking nodes in singly linked list
    E data;
    private static int size = 0;

    public ListNode() {
        data = null;
        next = null;
        current = null;
        if(size > 0) { //changed from prev != null because no code to make prev not null
            prev.setNext(this);
        }
        size++;
    }
    public ListNode(E value) {
        data = value;
        next = null;
        current = this;
        System.out.println("current is " + current);
        if(size > 0) {
            prev.setNext(current);//this line causing npe
        }
        else
        {
            prev = current;
            System.out.println("prev now set to " + prev);
        }
        size++;
        System.out.println("after constructor, size is " + size);
    }

    public ListNode(E value, ListNode<E> n) {
        data = value;
        next = n;
        current = this;
        if(size > 0) {
            prev.setNext(this);
        }
        size++;
    }

    public void setNext(ListNode<E> n) {
        next = n;
    }
}

就像现在一样,程序将一直运行到 prev.setNext(current);在 ListNode 的单参数构造函数中。在到达此行时,current 和 prev 都不为空。任何建议将不胜感激。谢谢。

【问题讨论】:

  • 如果方法不是静态的,它似乎是 LinkedList 类的一部分。然后它将能够访问(假定为私有的)字段头。
  • 我看到你已经继承了 java.util.LinkedList。子类化然后实现自己的节点类没有多大意义。 LinkedList 已经有一个名为 Entry 的私有类用于此目的。正如您提到的,您无权访问它,因此您无法使用 LinkedList 子类实现您展示的算法。正如我在回答中指出的那样,您必须创建自己的。
  • 如果您有兴趣了解 LinkedList 的工作原理,您可以在 developer.classpath.org/doc/java/util/LinkedList-source.html 上查看 LinkedList 的源代码。您会看到它实际上是一个双链表(next 和 prev 引用)。这是允许从中间移除项目所必需的。

标签: java nodes singly-linked-list


【解决方案1】:

您实际上并不需要单独的 LinkedList 类; ListNode 类 一个链表。或者,换一种说法,对列表头部的引用就是对列表的引用。

您发布的示例代码中对 head、tail、current、prev 的使用来自一个双向链接列表,该列表是一种双向链接的数据类型。这对于某些类型的应用程序(例如查找倒数第 n 个项目)更有效。

所以我建议将您的 ListNode 类重命名为 LinkedList 并将 next 重命名为 tail

要将新项目添加到列表中,您需要一个创建新列表的方法,该列表以新项目为开头。这是一个例子:

class LinkedList<E> {
    ...

    private LinkedList(E value, LinkedList<E> tail) {
        this.data = value;
        this.tail = tail;
    }

    public LinkedList<E> prependItem(E item) {
        return new LinkedList(item, this);
    }
}

然后添加一个新项目ilist 你使用list = list.prependItem(i);

如果由于某种原因您需要始终将项目添加到末尾,那么:

private LinkedList(E value) {
    this.data = value;
    this.tail = null;
}

public void appendItem(E item) {
    LinkedList<E> list = this;
    while (list.tail != null)
        list = list.tail;
    list.tail = new LinkedList<>(item);
}

但是,对于长列表,这显然是非常低效的。如果你需要这样做,那么要么使用不同的数据结构,要么在完成添加后反转列表。

顺便说一句,这样做的一个有趣的副作用是对列表中任何项目的引用都是对链接列表的引用。这使得递归非常容易。例如,这里有一个查找列表长度的递归解决方案:

public int getLength(LinkedList list) {
    if (list == null) {
        return 0;
    } else {
        return 1 + getLength(list.getTail());
    }
}

并使用这个简单(但效率非常低!)的解决方案来解决您提供的问题 - 我已重命名该方法以使其功能更明显:

public LinkedList getTailOfListOfLengthN(LinkedList list, int n) {
    int length = getLength(list);
    if (length < n) {
        return null;
    } else if (length == n) {
        return list;
    } else {
        return getTailOfLengthN(list.getTail(), n);
    }
}

并反转列表:

public LinkedList<E> reverse() {
    if (tail == null) {
        return this;
    } else {
        LinkedList<E> list = reverse(tail);
        tail.tail = this;
        tail = null;
        return list;
    }
}

我希望你能看到,这使得方法比分离节点列表类更优雅。

【讨论】:

  • 好的,你是说我的 ListNode 类已经是一个链表了。在那种情况下,我将如何将节点连接在一起以形成列表?我是否需要在 ListNode 中创建一个连接 current 和 tail 的方法,就像我在 MyLinkedList 的 addNode 方法中所做的那样?另外,你的主要方法是什么样的?
  • 我将在我的答案中添加一个方法,用于将新节点添加到列表中。
  • 请注意,除非您创建一个双链表,否则您不需要对尾部的引用。对于标准链表,新项目被推到顶部(这使得它们成为实现堆栈的良好候选者)。
【解决方案2】:

实际上你已经用你的 ListNode 类创建了一个链表。

链表由一个节点和对另一个链表的引用组成(见递归?)。

【讨论】:

  • 如果我要使用上面的 ListNode 类,我将如何将节点链接在一起?我是否必须创建 ListNode 元素的 LinkedList?我是否需要像这样创建一个尾节点和一个 size int 并运行一个 for 循环来将一个节点链接到它的下一个节点?:
  • for(int i = 0; i
猜你喜欢
  • 1970-01-01
  • 2014-02-13
  • 2016-01-24
  • 2013-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多