【问题标题】:Circular LinkedList in JavaJava中的循环链表
【发布时间】:2015-04-22 16:32:30
【问题描述】:

我正在通过阅读一本书来复习我的数据结构,它提出的一个问题是不使用“first”和“last”指针来构建一个循环单链表,而是允许通过使用一个参考“当前”。我不确定我是否理解这个问题,我一直认为我至少需要第一个或最后一个。这是我的实现,但它有“第一”,不知道如何绕过它。您能否评论一下我如何调整我的代码以消除对第一的依赖?

class Link {
    public int iData;              
    public Link next;              

    public Link(int id) { // constructor
        iData = id;                         
    }                          

    public void displayLink() {
        System.out.print(iData + " ");
    }
}  // end class Link

然后这是列表本身:

public class CircularLinkedList {
    private Link first;
    private Link current;    

    public Link getCurrent(){
        return current;
    }

    public void setCurrent(int data){

    }

    public void advance(){
        current = current.next;
    }

    public void insert(int data) {
        Link newLink = new Link(data);
        if (first == null) { 
            first = current = newLink;
        } else {
            current.next = newLink;
        }
        current = newLink;
        newLink.next = first;
    }
    ...
}

【问题讨论】:

    标签: java data-structures linked-list


    【解决方案1】:

    如果你有一个循环链表,那么每个节点都指向一个连续循环中的下一个节点。所以没有最后也没有第一个。在这种情况下,您只需要一个指向数据结构(问题称为“当前”)的指针,因为您可以遍历整个列表,直到回到您开始的位置。

    一个节点可能如下所示:

    public class ListNode<T> {
        private T element;
        private ListNode<T> next;
    }
    

    因此,在这种环境中,当您将第一个节点添加到列表中时,它的“下一个”指针将指向自身。当您添加第二个节点时,每个“下一个”指针将指向另一个。当您添加第三个节点时,它们将各自指向下一个节点,并且可能以某种方式对其进行排序。添加的每个附加节点都将插入到循环中的适当位置。

    像这样的数据结构的一个很好的用法是每天或每小时重复的时间表。所有事件都可以存储在一个循环列表中,并且“当前”指针始终指向下一个安排的事件。

    显然,在搜索这样的列表时,您需要保留对第一个节点的引用。这是您知道是否搜索过整个列表的唯一方法,因为它不会以空的“next”指针结尾。

    编辑:正如下面(广泛的)cmets 中所讨论的,“尾”指针在这里对于插入操作来说似乎是一个非常好的主意。下面是我贴的原方法,改名为insertAfter

    public void insertAfter(int data) {
        Link newLink = new Link(data);
        if (current == null) { 
            newLink.next = newLink;
            current = newLink;
        } else {
            newLink.next = current.next;
            current.next = newLink;
        }
    }
    

    尾指针将始终指向列表的最后一个节点。这也将是第一个节点之前的节点,因为列表是循环的。所以在操作指针时一定要维护(tail.next == current)。这是新的插入方法:

    public void insert(int data) {
        Link newLink = new Link(data);
        if (current == null) { 
            current = newLink;
            tail = newLink;
            newLink.next = newLink; // tail.next = current!
        } else {
            tail.next = newLink; // tail.next = current!
            newLink.next = current;
            current = newLink; // tail is unchanged, newLink is current
        }
    }
    

    插入值应该正确地将它们弄乱。添加时要保持顺序,应使用add 方法:

    public void add(int data) {
        this.insert(data);
        this.advance();
    }
    

    这是advance的代码:

    public void advance() {
        if (current != null) {
            tail = current;
            current = tail.next; // tail.next = current!
        }
    }
    

    当这样用于创建列表时...

    list1.add(1);
    list1.add(2);
    list1.add(3);
    list2.insert(3);
    list2.insert(2);
    list2.insert(1);
    

    ...两个列表都按 1-2-3 排序,其中 1 为当前列表。

    【讨论】:

    • 所以,insertAfter 和 DeleteAt 等操作将正常执行,假设您有对第一个(“当前”)的引用,当前必须是第一个节点,还是可以在任何地方?
    • 听起来“当前”可以在任何地方,因为没有“第一个”节点。如果数据定义需要一个明确的“第一个”节点,那么使用循环列表是不合适的。在循环链表中,使用索引来标识节点是没有意义的。例如,如果您拨打deleteAt(2) - 那是从哪里来的2?什么是零?
    • 对于循环链表,任何基于索引执行操作的方法都必须使用相对于“当前”节点的索引。那是因为这是数据结构的唯一入口点。
    • 这正是让我感到困惑的地方,因为我在问题中遗漏的是,它还要求列表还应该执行所有正常操作,如查找、删除、插入..ect
    • 这些操作必须稍作修改才能有意义。删除和插入操作都将在当前之后删除和插入。如果您尝试删除当前,您将不得不遍历整个单链接数组,这是非常低效的。在之前插入也是如此。
    【解决方案2】:

    使用Node current, int currentIndex, int size。让最后一个节点的next 指向列表的第一个节点(= 循环)。使用当前索引,您可以遍历 (size - 1) 个元素以到达 current 的上一个节点。

    【讨论】:

      【解决方案3】:

      由于链表是循环的,所以没有第一个和最后一个元素。

      您可以从任何节点(在此上下文中为当前节点)开始遍历整个列表。

      所以Node 类只有下一个引用,CircularLinkedList 只有当前引用。

      希望这会有所帮助。
      祝你好运。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-02
        • 1970-01-01
        • 1970-01-01
        • 2021-07-05
        • 2014-12-28
        • 1970-01-01
        相关资源
        最近更新 更多