【问题标题】:Error in tracking lastNode at removing the Cycle of Nodes in LinkedLIst in java在 java 中删除 LinkedLIst 中的节点循环时跟踪 lastNode 时出错
【发布时间】:2023-01-30 05:25:03
【问题描述】:

我创建了一些带有 lastNode 指向头的节点。 在 removeCycle 方法中,当我尝试使 lastNode(i,e prev).next = null 时,首先检测到 lastNode 然后出错

public class loopsRemove {
    public static class Node{
        int data;
        Node next;

        public Node(int data){
            this.data = data;
            this.next = null;
        }
    }

    public static Node Head;
    public static Node Tail;
    public static int count =0;

   
    public static int removeCycle(){
        Node slow = Head;
        Node fast = Head;
          boolean Cycle = false;
    

        while(fast !=null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            count++;
            if(slow == fast){
                Cycle =true;
               break;
            }
        }
        if(Cycle == false){
            return 0;  //No Cycle and come out of function (int type is just to observe where function is returning
        }
        

        slow = Head;
        Node prev=null;  //to track previous of fast
        while(slow != fast){
            prev = fast;
            slow = slow.next;
            fast = fast.next;  //speed is same as slow now
        }
        prev.next =null;  //Making endNode.next to null
        return 1;   //int return is just to check weather my code is returning here or above
    }
 
    public static void main(String[] args) {
        Head = new Node(3); 
        Head.next = new Node(4);
        Head.next.next = new Node(5);
        Head.next.next.next = new Node(6);
        Head.next.next.next.next = Head;  //cycle formed
        System.out.println(removeCycle());

       
        System.out.println(Head.next.next.next.next.data); // null is expected at the last node if removeCycle works correctly
    }
}

预期输出: 1个 无效的

当前输出: 线程“main”中的异常 java.lang.NullPointerException:无法分配字段“next”,因为“prev”为空 在 loopsRemove.removeCycle(loopsRemove.java:44) 在 loopsRemove.main(loopsRemove.java:55)

【问题讨论】:

    标签: java algorithm data-structures linked-list


    【解决方案1】:

    您的算法适用于在任何地方打破循环,除非循环包含头节点。

    要解决这个问题,您可以在头部之前临时添加一个虚拟节点前缀,在这个较长的列表上使用该算法,然后再次省略该虚拟节点。

    以下代码是您的代码,除了引用 dummy 的行:

        public static int removeCycle() {
            Node dummy = new Node(0); 
            dummy.next = Head; // Prefix a dummy node before the head node
            Node slow = dummy; // Apply the algorithm on this longer list
            Node fast = dummy;
            boolean Cycle = false;
    
            while(fast !=null && fast.next != null){
                slow = slow.next;
                fast = fast.next.next;
                count++;
                if(slow == fast){
                    Cycle =true;
                   break;
                }
            }
            if(Cycle == false){
                return 0;
            }
            
    
            slow = dummy;  // Start at the temporary head
            Node prev=null;
            while(slow != fast){
                prev = fast;
                slow = slow.next;
                fast = fast.next;
            }
            prev.next =null;
            return 1;
        }
    

    请注意,在您的主程序中,您不应尝试打印 null 引用的数据。只需打印预期为 nullnext 属性:

            System.out.println(Head.next.next.next.next);
    

    因为虚拟节点除了局部变量外没有被其他任何东西引用,它可以在removeCycle返回后被垃圾回收。

    【讨论】:

    • 我看不出第二个 while: while(slow != fast){ prev = fast;慢 = 慢.next;快速 = 快速.下一个; //speed is same as slow now } 可以保留。如果我错了,请解释。
    • 我在解释中理解了你的“第一行”,但是你能说出将快速和慢速初始化到 head 之前的节点背后的逻辑吗?当我的 Cycle 排除 Head 时,相同的代码是否有效?
    • 是不是我的代码在包含 head 时不起作用,因此在 Node 前面加上前缀,这样 head 就变成了非 Head 部分,而我的循环在非 head 部分重新加入..?
    • 把它想象成你真的有一个以虚拟头作为它的新头的列表。然后就像您在代码中所做的那样:从那个头开始。不同之处在于,现在我们知道(新)头不会成为循环的一部分。这是必需的,因为如果原始磁头是循环的一部分,则该算法无法完成这项工作。通过在前面添加一个新的头部,可以避免这种情况。现在它将适用于所有情况。
    【解决方案2】:

    第一个 while 结束时

            if(slow == fast){
                Cycle =true;
               break;
            }
    

    然后

        slow = Head;
    

    没有改变任何东西,在这种情况下 (3->4->5->6) slow 已经是 head,slow==fast 仍然是 true 并且永远不会输入以下 while

        Node prev=null;  //to track previous of fast
            while(slow != fast){
                 prev = fast;
                 slow = slow.next;
                 fast = fast.next;  //speed is same as slow now
             }
             prev.next =null;  
    

    这意味着当您尝试分配 prev.next 时 prev 为空。

    编辑: 如果没有虚拟对象,您的代码将无法运行的原因是,当没有不属于圆圈的元素时,第一个 while 在 slow==fast==Head 时停止,这意味着对 head 的下一个引用是丢失的。在 head 之前的元素处使用 dummy,slow==fast(如果没有非圆形元素,否则更早),以便 prev.next 可以重新分配为 null。而且,没有跳过第二个 while 的风险。 (这也可以通过使用 do-while 来避免。)

    【讨论】:

    • while(node.next!=null && node.next !=Head) { node=node.next; } 当我的循环在头部(即第一个节点)重新加入时,这有效如果循环在中间的任何地方加入怎么办,那么这将无法正常工作..!?
    • 没错,如果链表看起来像数字6,一个带尾巴的圆圈,那是行不通的……
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-25
    • 2019-01-13
    相关资源
    最近更新 更多