简体   繁体   English

Java使用带有LinkedList的节点

[英]Java Using Nodes with LinkedList

I've been working through some standard coding interview questions from a book I recently bought, and I came across the following question and answer: 我一直在研究我最近购买的一本书中的一些标准编码面试问题,并且遇到以下问题和答案:

Implement an algorithm to find the nth to last element in a linked list. 实现一种算法,以找到链表中的第n个元素。

Here's the provided answer: 这是提供的答案:

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;
    }

I understand the algorithm, how it works, and why the book lists this as its answer, but I'm confused about how to access the LinkedListNodes to send as an argument to the method. 我了解该算法,其工作原理以及为什么书将其列出为答案,但我对如何访问LinkedListNodes作为方法的参数发送感到困惑。 I know that I'd have to create a LinkedListNode class (since Java doesn't already have one), but I can't seem to figure out how to do that. 我知道我必须创建一个LinkedListNode类(因为Java还没有一个类),但是我似乎无法弄清楚该怎么做。 It's frustrating because I feel like I should know how to do this. 这令人沮丧,因为我觉得我应该知道该怎么做。 Here's something that I've been working on. 这是我一直在努力的事情。 I'd greatly appreciate any clarification. 如有任何澄清,我将不胜感激。 You can expand/comment on my code or offer your own alternatives. 您可以扩展/评论我的代码,也可以提供自己的选择。 Thanks. 谢谢。

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;
    }


}

How can the LinkedListNode head from a LinkedList? LinkedListNode如何从LinkedList前进?

Update: I think part of my confusion comes from what to put in the main method. 更新:我认为我的部分困惑来自主要方法中的内容。 Do I need to create a LinkedList of ListNode? 我是否需要创建ListNode的LinkedList? If I do that, how would I connect the ListNodes to each other? 如果这样做,我将如何将ListNode彼此连接? How would I connect them without using a LinkedList collection object? 在不使用LinkedList集合对象的情况下如何连接它们? If someone could show me how they would code the main method, I think that would put things into enough perspective for me to solve my issues. 如果有人可以告诉我他们如何编写主要方法,那么我认为这将使我有足够的视角来解决问题。 Here's my latest attempt at the main method: 这是我对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);
    }

In an attempt to connect the nodes without using a custom linked list class, I have edited my ListNode class to the following: 为了在不使用自定义链接列表类的情况下连接节点,我将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;
    }
}

As is right now, the program will run until it reaches prev.setNext(current); 现在,程序将运行直到达到prev.setNext(current);。 in the single argument constructor for ListNode. 在ListNode的单个参数构造函数中。 Neither current nor prev are null at the time this line is reached. 达到此行时,current和prev都不为空。 Any advice would be greatly appreciated. 任何建议将不胜感激。 Thanks. 谢谢。

You don't actually need a separate LinkedList class; 您实际上不需要单独的LinkedList类; the ListNode class is a linked list. ListNode类一个链表。 Or, to state it differently, a reference to the head of the list is a reference to the list. 或者,换句话说,对列表头的引用是对列表的引用。

The use of head, tail, current, prev in the sample code you posted has come from a double-linked list which is a data type that has links in both directions. 您发布的示例代码中使用head,tail,current,prev来自双重链接列表,该列表是一种双向双向链接的数据类型。 This is more efficient for certain types of applications (such as finding the nth last item). 对于某些类型的应用程序(例如查找第n个最后一项),这更有效。

So I would recommend renaming your ListNode class to LinkedList and renaming next to tail . 因此,我建议将您的ListNode类重命名为LinkedList并在tail next重命名。

To add a new item to the list you need a method that creates a new list with the new item at it's head. 要将新项目添加到列表中,您需要一个方法来创建一个新列表,其中新项目位于其顶部。 Here is an example: 这是一个例子:

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);
    }
}

Then to add a new item i to list you use list = list.prependItem(i); 然后,要添加新项ilist请使用list = list.prependItem(i);

If for some reason you need to always add the items to the end, then: 如果出于某种原因您需要始终将项目添加到末尾,则:

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);
}

However this is obviously pretty inefficient for long lists. 但是,对于长列表来说,这显然效率很低。 If you need to do this then either use a different data structure or just reverse the list when you have finished adding to it. 如果需要执行此操作,则可以使用其他数据结构,也可以在添加完毕后反转列表。

Incidentally, an interesting side effect of this is that a reference to any item in the list is a reference to a linked list. 顺便说一下,这样做的一个有趣的副作用是,对列表中任何项目的引用都是对链接列表的引用。 This makes recursion very easy. 这使得递归非常容易。 For example, here's a recursive solution for finding the length of a list: 例如,这是一个用于查找列表长度的递归解决方案:

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

And using this a simple (but very inefficient!) solution to the problem you provided - I've renamed the method to make its function more obvious: 并使用此简单(但效率很低!)解决方案来解决您提供的问题-我将方法重命名为使其功能更加明显:

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);
    }
}

And to reverse the list: 并反转列表:

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

As I hope you can see this makes the methods a lot more elegant than separating the node list classes. 我希望您能看到,这使方法比分离节点列表类要优雅得多。

Actually you have created a linked list with you class ListNode. 实际上,您已经使用ListNode类创建了一个链接列表。

A linked list is made of a node and a reference to another linked list (see the recursion?). 链表由节点和对另一个链表的引用组成(请参阅递归?)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM