【问题标题】:Linked list with recursion带递归的链表
【发布时间】:2021-03-23 15:06:00
【问题描述】:

早上好,我正在学习,我想用递归函数制作一个链表,我一直在寻找示例,但我只找到了 Java 或其他类型的示例。 从我所看到的情况来看,我想它会是这样的,但我无法让它工作。如果有人可以帮助我,我将不胜感激。 谢谢。


function ListRecurse () {
    this.head = null;
    this.size = 0;
}
function Node () {
    this.data = data; 
    this.next = next;
}
ListRecurse.prototype.add = function (data) {
    let NewNode = new Node(this.add(data), null)
    if (this.head === null) {
        this.head = NewNode;
    }
    else {
        let current = this.head;
        while (current.next) {
            current = current.next;
        }
        current = NewNode
    }
    this.size ++;
}

【问题讨论】:

  • 在您的Node 构造函数中,您使用的是datanext 变量,但这些变量在任何地方都没有定义。您需要使用函数参数。另外,你的add 方法做的第一件事就是再次调用add,它将是无限的
  • 你有没有一个递归函数调用的例子和它想要的结果与多个节点?

标签: javascript recursion linked-list


【解决方案1】:

您可以利用存储最后一个节点并返回this 以获得fluent interface

function ListRecurse() {
    this.head = null;
    this.last = null;
    this.size = 0;
}

function Node(data, next) {
    this.data = data;
    this.next = next;
}

ListRecurse.prototype.add = function(data) {
    const node = new Node(data, null);
    if (this.head === null) this.head = node;
    else this.last.next = node;
    this.last = node;  
    this.size++;
    return this;
}

const list = new ListRecurse().add(1).add(2).add(3);

console.log(list);
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

    【解决方案2】:

    您的实施中有几个问题:

    1. 你使用递归会导致堆栈溢出:

          let newNode = new Node(this.add(data));
      

      这只会不停地调用自己。您在此语句下方的代码将永远不会被执行。递归总是需要一个基本情况,这将停止递归。

    2. Node 构造函数缺少参数,因此变量datanext 不存在。

    3. 分配NewNode 时,不应将其分配给current,否则会破坏列表。你应该把它分配给current.next

    解决了这些问题,它会起作用,但也要考虑到以下备注:

    • 不要以首字母大写命名变量,因为通常的做法是为构造函数(类)保留这种符号。所以我会使用名称newNode 而不是NewNode
    • 对可能不会在调用中使用的参数使用默认值。在你的情况下,你可以给next一个默认值null,这样你就不需要在Node的调用中指定null
    • 为您的构造函数使用现代的class 语法。这会导致代码更简洁。
    • 允许链接add 方法。所以让它返回this

    所以递归实现可能如下所示:

    class Node {
        // Accept arguments (the second one could be optional)
        constructor(data, next=null) {
            this.data = data; 
            this.next = next;
        }
        lastNode() { // new method that uses recursion
            return this.next?.lastNode() || this;
        }
    }
    
    
    class ListRecurse {
        constructor() {
            this.head = null;
            this.size = 0;
        }
        add(data) {
            let newNode = new Node(data); // No second argument. It has a default value
            if (this.head === null) {
                this.head = newNode;
            } else {
                // The lastNode implementation uses recursion:
                this.head.lastNode().next = newNode;
            }
            this.size ++;
            return this; // to allow chaining
        }
    }
    
    let list = new ListRecurse();
    list.add(1).add(2).add(3);
    console.log(list);

    递归现在发生在这个语句中,这可能需要一些解释:

    return this.next?.lastNode() || this;
    

    这将检查当前节点是否有下一个节点引用。如果是这样,则对lastNode 进行递归调用。如果不是,那么我们处于“基本情况”:带有可选链接运算符 (?.) 的表达式是 undefined,然后 OR 运算符 (||) 将启动以返回 this,因为它是链中的最后一个节点。

    更高效

    首先,在这里使用递归可能是一个很好的练习,但它并没有带来太多好处。迭代解决方案很好。

    遗憾的是,每次添加节点时都需要遍历整个链表。正如@Nina 已经回答的那样,您可以通过维护对尾节点的引用来避免这种低效率。

    【讨论】:

      猜你喜欢
      • 2017-04-21
      • 1970-01-01
      • 1970-01-01
      • 2021-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-30
      相关资源
      最近更新 更多