Java 反转链表的前K个节点,为什么在最后一次迭代中执行两次递归

Java 反转链表的前K个节点,为什么在最后一次迭代中执行两次递归,java,algorithm,recursion,data-structures,linked-list,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 -&g

在解决反转链表的前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”条件中使用变量“k”而不是“presentCounter”时,它就错了。谁能告诉我原因吗

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;
}
public void reverseListRecursion(整数计数){
this.reverseNode(null,headNode,count);
System.out.println(“\n”);
this.display(this.headNode);
}
/*
*条件K(1){
k=k-1;
this.reverseNode(nextNode,nextNode.next,k);
}
if(presentCounter==1){
this.kNode=nextNode.next;//保存K的下一个节点
this.headNode=nextNode;//将K节点设置为head节点
}
if(node==null){
nextNode.next=this.kNode;
}否则
nextNode.next=节点;
}

您的递归应该是

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

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

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;
    }
}
  • wile循环将使递归调用的数量增加到k!。也许这是你的意图,但它肯定不会成为一个有效的算法。这项工作可以通过移动k-1节点来完成,因此,使k!打电话效率不高

  • 第一个参数(节点)永远不会更改:递归调用只传递相同的参数,因此在初始调用中传递的值(
    null
    )就是每次调用中该参数的值。因此,最后一个
    if
    条件的结果将始终相同。这看起来不对

  • 第一个
    if
    条件将始终为true,因为它与前面的
    条件相反,而
    条件在前面。因此,不需要对
    k==0
    进行测试

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

  • 在第二个
    if
    块中,
    nextNode
    next
    属性设置为
    nextNode
    (参见上一点,它表明
    this.kNode
    nextNode
    的同义词)。现在你有了一个自引用节点,这是你永远都不想拥有的。此代码使第一个k+1节点自引用,从而有效地将它们从原始链接列表中分离出来

  • 初始调用以headNode作为第二个参数。此变量显然是您所在类的私有成员。但是,在执行反转后,headNode仍将引用调用前引用的节点。名称表明它应该指向列表中的第一个节点,但由于反转将移动前面的另一个节点,因此完成后headNode将指向错误的节点。没有其他变量或属性会指向反转后列表中的第一个节点。this.kNode可能就是它,但是语句
    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
    
    list = 3 2 1 4 5 6 7 8 
    k = 2
    
    list = 4 3 2 1 5 6 7 8 
    k = 1
    
    将原始第一个节点之后的节点移动到列表的开头

    list = 2 1 3 4 5 6 7 8 
    k = 3
    
    list = 3 2 1 4 5 6 7 8 
    k = 2
    
    list = 4 3 2 1 5 6 7 8 
    k = 1
    
    将原始第一个节点之后的节点移动到列表的开头

    list = 2 1 3 4 5 6 7 8 
    k = 3
    
    list = 3 2 1 4 5 6 7 8 
    k = 2
    
    list = 4 3 2 1 5 6 7 8 
    k = 1
    
    当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);
    };
    
    这将反转给定节点后面的节点

    下面是一个实时片段,其中的代码被翻译为JavaScript(仅用于演示):

    //节点类
    功能节点(val,下一个){
    this.val=val;
    this.next=next;
    this.toString=函数(级联){
    如果(!cascade | | this.next==null)返回“(“+this.val+”)”;
    如果(this.next==this)返回'('+this.val+')-loop';
    返回'('+this.val+')->'+this.next.toString(true);
    }
    }
    //列表类
    函数列表(){
    this.headNode=null;
    this.reverseListRecursion=函数(k){
    //反转返回第一个位置的节点
    this.headNode=this.reverseNode(this.headNode,k,this.headNode);
    };
    this.reverseNode=函数(origFirstNode,k,currFirstNode){
    //如果需要反转1个元素,则无需执行任何操作:
    如果(k=0;i--){
    this.headNode=新节点(arr[i],this.headNode);
    }
    }
    this.toString=函数(){
    返回'{'+this.headNode.toString(true)+'}';
    }
    }
    var输出=[];
    //样本数据
    var list=新列表();
    列表。插入([1,2,3,4,5,6,7,8]);
    output.push('before:'+list);
    //打反向电话
    列表。反向撤销(4);
    output.push('after:'+list);
    //在代码段中显示结果
    
    document.write(输出.join(“
    ”)(请给出注释代码,说明您不满意的地方,并尝试将示例与代码分开。)我不确定我是否理解该问题;实现的输出是否如预期的那样?似乎是这样。只需在调试器中单步执行即可查看发生了什么。您最好使用递归或循环,但不能同时使用两者。尝试将
    while
    替换为
    if