【问题标题】:Javascript: Detect and Remove a loop from cylic linked listJavascript:从循环链表中检测并删除循环
【发布时间】:2015-12-01 23:08:10
【问题描述】:

我使用 Javascript 编写了一个循环链表并检测和删除循环。在循环检测部分之前它工作正常。它如何无法删除循环节点。更具体地说:此代码的 removeLoop 功能不起作用。

这是我的代码:

    function Node(element){
        this.element = element;
        this.next = null;
    }

    //circular linked list class

    function LList() {
        this.head = new Node("head");
        this.head.next = this.head;
        this.find = find;
        this.insert = insert;
        this.display = display;


    }

    function find(item){
        var curr = this.head;
        while(curr.element != item){
            curr = curr.next;
        }
        return curr;
    }

//inserting items into linked list

    function insert(newElem, after){
        var newNode = new Node(newElem);
        var curr = this.find(after);
        newNode.next = curr.next;
        curr.next = newNode;
    }

    function display() {
        var currNode = this.head;
        while ((currNode.next !== null) &&
        (currNode.next.element !== "head")) {
            console.log(currNode.next.element);
            currNode = currNode.next;
        }
    }

    function findPrevious(item){
     var curr = this.head;
        while(curr.next !== null && curr.next.element !== item){
            curr =curr.next;
        }
        return curr;
    }

    //creating a linkedlist object

    var furniture = new LList();
    furniture.insert("chair","head");
    furniture.insert("table", "chair");
    furniture.insert("couch", "table");
    furniture.insert("stool","couch");
    //furniture.display();

    //detecting if a linked list is circular

    function detectALoop(list){
        var slow = list.head;
        var fast = list.head;
        while(slow && fast && fast.next){
            slow = slow.next;
            fast = fast.next.next;

            if(slow === fast){
               removeLoop (slow, list);
                return 1;
            }
        }
        return 0;
    }

    //This part of the code doesnot work

    function removeLoop(loopNode, list)
    {
        var ptr1 = loopNode;
        var ptr2 = loopNode;
        var looplen = 1,i;


        // count the number of nodes in loop

        while(ptr1.next != ptr2)
        {
            ptr1 = ptr1.next;
            looplen++;
        }
        console.log(looplen)
        ptr1 = list.head;
        ptr2 = list.head;
        for(i=0; i <= looplen; i++)
        {
            ptr2 = ptr2.next;
        }



        while(ptr2.next != ptr1.next)
        {
            ptr1 = ptr1.next;
            ptr2 = ptr2.next;
        }

        ptr2.next = null; // breaking the loop
    }


    console.log(detectALoop(furniture))
    furniture.display();

【问题讨论】:

  • 列表是一个循环是您设计的一部分吗?有没有办法最后一个节点会指向中间?如果没有,我认为可以删除很多内容。例如,列表不需要对头节点的两个引用,而是将节点存储在末尾。这使得这非常简单。 ..
  • 我的代码,在 LList 类中:this.head = new Node("head"); this.head.next = this.head;这是为了使列表循环。当我检查循环是否是循环时,我得到了真实的结果。在我的情况下,循环位于列表的开头。我正在尝试删除循环节点,我需要一个通用算法,无论循环在哪里,它都可以工作。
  • 好的,如果最后一个节点指向列表的中间,这是否需要工作?我仍然认为循环比你需要的多。
  • 考虑使用双链表来加快查找上一个项目。

标签: javascript algorithm linked-list


【解决方案1】:

如果循环必须回到第一个元素上,那么你会让这变得比它需要的复杂得多。

function breakLoop(list) {
    var head = list.head, tail = head, len = 1;
    while (tail.next != head) {
        len++;
        tail = tail.next;
    }
    tail.next = null;
    console.log(len.toString());
}

现在,如果您可能需要处理任意循环,我仍然不知道您需要 3 个循环来做什么。使用 ES6 Set;我相信大多数浏览器现在都支持这一点。我将继续返回长度而不是记录它。

function breakLoopAnywhere(list) {
    var seen = new Set, node = list.head;
    while (!seen.has(node.next)) {
        seen.add(node);
        node = node.next;
    }
    node.next = null;
    return seen.size;
}

如果你没有集合,你可以用一个数组来破解它,用indexOf替换has,用push替换add

如果你觉得你必须有能力检测循环和非循环列表而不破坏它:

// takes a node, returns the node 
// that points backwards on its next
function getLoopNode(node) {
    var seen = new Set; 
    do {
        seen.add(node);
    } while (!seen.has(node.next) && node = node.next)
    return node;
}

function detectLoop(node) {
    return getLoopNode(node) != null;
}

function breakLoop(node) {
    node = getLoopNode(node);
    if (node) node.next = null;
}

您的detectALoop 没有那么复杂,但它是错误的。这将检测到的唯一循环是节点2i 是否循环回到节点i。但是列表可能是 3 个元素长循环到开头;可能有很多不是2ii 的数字。由于可能有很多数字,太多了,无法全部尝试,因此您无法解决此策略。没有比我上面写的更快或更直观的方法来找到图中的循环。据我所知。

【讨论】:

    【解决方案2】:

    这个变量搞砸了……

    var looplen = 1,i;
    

    看起来你希望它是 1。

    【讨论】:

    • 我将 looplen 更改为 0 而不是 1(仍然无法正常工作)。我在代码的下一部分计算循环长度,它达到 4(如预期的那样)。我将 ptr1 放在循环的开头,将 ptr2 放在循环的结尾。然后检查(ptr1.next == ptr2.next)。在这种情况下,我想打破循环
    【解决方案3】:

    您的removeLoop 代码错误,它永远不会终止:

    让我们假设这个列表:

    A -> B -> C -> A
    

    循环长度为 3。

    你正确找到了循环长度3,然后将ptr1ptr2设置为列表的头部,然后在ptr2上调用.next以获得循环长度+1次(因为&lt;=)。

    // for i = 0; i <= 3
    
    A.next -> B // i = 0
    B.next -> C // i = 1
    C.next -> A // i = 2
    A.next -> B // i = 33
    

    所以最后你有ptr2 = B 和ptr1 = A,即ptr2 === ptr1.next

    一个是另一个的下一个,并且在 while 循环中,您会推进两个,直到一个等于另一个,但它们永远不会是,因为它们始终是另一个的下一个!

    如果您将&lt;= 更改为仅&lt; 它可以工作,但第二个while 循环实际上是无用的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-05
      • 2011-12-04
      • 2015-07-31
      • 2012-01-06
      • 2012-06-18
      相关资源
      最近更新 更多