反转链接列表的前K个节点,为什么递归执行两次以进行最后一次迭代

时间:2016-04-16 18:55:33

标签: java algorithm recursion data-structures linked-list

在解决链接列表的第一个K元素的反转问题时,我编写了下面的递归代码,但最后一次迭代执行了两次,即k = 1,函数调用reverseNode()发生了两次。任何身体都可以这样发生。如果我做错了,请纠正我。

Example : 

    If input is 

    1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 

    and k = 4 then output is

    4 -> 3 -> 2 -> 1 -> 5 -> 6 -> 7 -> 8 

public void reverseListRecursion(int k) {
    this.reverseNode(null, headNode,k);
}

public void reverseNode(Node node, Node nextNode,int k) {
    while (k > 0) {
        k = k-1;
        this.reverseNode(node, nextNode.next,k);
    }

    if (k == 0) {
        this.kNode = nextNode;
        this.kNode.next = null;
    }

    if (node == null) {
        nextNode.next = this.kNode;
    } else {
        nextNode.next = node;
    }
}

我的逻辑的工作代码正在工作中。但是当我尝试在“if”条件而不是“presentCounter”中使用变量“k”时,它就会出错。任何人都可以告诉我原因。

public void reverseListRecursion(int count) {
    this.reverseNode(null, headNode, count);
    System.out.println("\n");
    this.display(this.headNode);
}

/*
 * Condition K <= Length of linked list.
 */
public void reverseNode(Node node, Node nextNode, int k) {
    int presentCounter = k;
    if (k > 1) {
        k = k - 1;
        this.reverseNode(nextNode, nextNode.next, k);
    }
    if (presentCounter == 1) {
        this.kNode = nextNode.next; // Saving K's Next Node
        this.headNode = nextNode; // Setting K node as head node
    }
    if (node == null) {
        nextNode.next = this.kNode;
    } else
        nextNode.next = node;
}

2 个答案:

答案 0 :(得分:0)

你的递归应该是

if (k > 0) {  // and not while 
    k = k-1;
    this.reverseNode(node, nextNode.next,k);
}
...

答案 1 :(得分:0)

您在问题中提供的此代码存在以下几个问题:

public void reverseListRecursion(int k) {
    this.reverseNode(null, headNode,k);
}

public void reverseNode(Node node, Node nextNode,int k) {
    while (k > 0) {
        k = k-1;
        this.reverseNode(node, nextNode.next,k);
    }
    if (k == 0) {
        this.kNode = nextNode;
        this.kNode.next = null;
    }
    if (node == null) {
        nextNode.next = this.kNode;
    } else {
        nextNode.next = node;
    }
}
  1. wile循环将使递归调用的数量增加到 k!。也许这是你的意图,但它肯定不会成为一个有效的算法。可以通过移动 k-1 节点完成这项工作,因此使 k!调用效率不高。

  2. 第一个参数( node )永远不会改变:递归调用只传递相同的参数,所以你在初始调用中传递的值(null)是什么每个电话都会有这个论点。因此,最后if条件的结果将始终相同。这看起来并不正确。

  3. 第一个if条件将始终为真,因为它与之前的while条件相反。因此,不需要为k == 0进行测试。

  4. 第一个if块中的代码使this.kNode成为nextNode的同义词,然后其next属性设置为null。根本没有理由将next属性设置为null。如果有的话,它会打破链表。

  5. 在第二个if块中,next的{​​{1}}属性设置为... nextNode(请参阅上一点,其中显示{{1} } {}为nextNode)的同义词。所以现在你有一个自引用节点,这真的是你永远不想拥有的。此代码使第一个k + 1个节点自引用,从而有效地将它们从原始链表中分离出来。

  6. 使用 headNode 作为第二个参数进行初始调用。此变量显然是您所在类的私有成员。但是,执行反转后, headNode 仍将引用它在调用之前引用的节点。该名称表明它应该指向列表中的第一个节点,但由于反转将移动前面的另一个节点, headNode 将在完成后指向错误的节点。没有其他变量或属性将指向反转后列表中的第一个节点。 this.kNode 可能是它,但是this.kNodenextNode语句不是您对列表的第一个节点所做的事情。

  7. 此代码存在太多问题,无法清楚地了解您实际尝试的内容。

    我建议使用这个算法,例如:

    this.kNode.next = null

    将原始第一个节点后面的节点移动到列表的头部

    nextNode.next = this.kNode

    将原始第一个节点后面的节点移动到列表的头部

    list = 1 2 3 4 5 6 7 8
    k = 4
    

    将原始第一个节点后面的节点移动到列表的头部

    list = 2 1 3 4 5 6 7 8 
    k = 3
    

    当k = 1时,不必再进行任何移动。

    这就是你的代码的样子:

    list = 3 2 1 4 5 6 7 8 
    k = 2
    

    这个解决方案的好处是list = 4 3 2 1 5 6 7 8 k = 1 方法不需要引用public void reverseListRecursion(int k) { // The reversal returns the node that took the first position this.headNode = this.reverseNode(this.headNode, k, this.headNode); }; public Node reverseNode(Node origFirstNode, int k, Node currFirstNode) { // If 1 element needs to be reversed, there is nothing to do: if (k <= 1) return currFirstNode; // Move the node after the original first node before the current first node Node movingNode = origFirstNode.next; origFirstNode.next = movingNode.next; movingNode.next = currFirstNode; // The moved node is now the current first node. Repeat: return this.reverseNode(origFirstNode, k-1, movingNode); }; ,因此它也可以用来反转列表中间的元素。您可以添加此方法,该方法将节点作为第二个参数:

    reverseNode

    这将反转给定节点之后的节点。

    这是一个实时代码段,将相同的代码转换为JavaScript(仅用于演示):

    &#13;
    &#13;
    this.headNode
    &#13;
    &#13;
    &#13;