为啥我的链表代码在模块化可重用 JavaScript 函数中不起作用?
Posted
技术标签:
【中文标题】为啥我的链表代码在模块化可重用 JavaScript 函数中不起作用?【英文标题】:Why does my linked list code not work in a modular reusable JavaScript function?为什么我的链表代码在模块化可重用 JavaScript 函数中不起作用? 【发布时间】:2022-01-13 12:19:23 【问题描述】:当我使我的代码更加模块化时,为什么我的链表解决方案会失败?
我正在处理链表格式的question 2 on Leetcode“添加 2 个数字”。
问题来了:
给定两个代表两个非负整数的非空链表。这些数字以相反的顺序存储,并且它们的每个节点都包含一个数字。将两个数字相加并以链表的形式返回总和。
您可以假设这两个数字不包含任何前导零,除了数字 0 本身。
我有一个包含重复代码的有效解决方案。 当我将最后两个函数的主体抽象为可重用函数时,我的解决方案失败了。这是为什么呢?
我做的一个假设是链表节点是对象,因此应该通过引用传递。至少在 javascript 中。
这是我成功的解决方案(在使代码更加模块化之前)...
var addTwoNumbers = function(l1, l2)
let l3 = new ListNode(0, null);
let head = l3;
let carry = 0;
while (l1 != null && l2 != null)
l3.next = new ListNode(carry, null);
l3 = l3.next;
let sum = l1.val + l2.val + l3.val;
let ones = sum % 10;
carry = Math.floor(sum / 10);
l3.val = ones;
l1 = l1.next;
l2 = l2.next;
while (l1 != null)
l3.next = new ListNode(carry, null);
l3 = l3.next;
let sum = l1.val + l3.val;
let ones = sum % 10;
carry = Math.floor(sum / 10);
l3.val = ones;
l1 = l1.next;
while (l2 != null)
l3.next = new ListNode(carry, null);
l3 = l3.next;
let sum = l2.val + l3.val;
let ones = sum % 10;
carry = Math.floor(sum / 10);
l3.val = ones;
l2 = l2.next;
if (carry == 1)
l3.next = new ListNode(carry, null);
return head.next;
;
这是我失败的解决方案...
/**
* Definition for singly-linked list.
* function ListNode(val, next)
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
*
*/
/**
* @param ListNode l1
* @param ListNode l2
* @return ListNode
*/
var addTwoNumbers = function(l1, l2)
let l3 = new ListNode(0, null);
let head = l3;
let carry = 0;
while (l1 != null && l2 != null)
l3.next = new ListNode(carry, null);
l3 = l3.next;
let sum = l1.val + l2.val + l3.val;
let ones = sum % 10;
carry = Math.floor(sum / 10);
l3.val = ones;
l1 = l1.next;
l2 = l2.next;
while (l1 != null)
carry = addRemainingNodes(l1, l3, carry);
while (l2 != null)
carry = addRemainingNodes(l2, l3, carry);
if (carry == 1)
l3.next = new ListNode(carry, null);
return head.next;
;
function addRemainingNodes(la, lb, carry)
lb.next = new ListNode(carry, null);
lb = lb.next;
let sum = la.val + lb.val;
let ones = sum % 10;
carry = Math.floor(sum / 10);
lb.val = ones;
la = la.next;
return carry;
【问题讨论】:
【参考方案1】:我做的一个假设是链表节点是对象,因此应该通过引用传递。
它们确实是对象。变量可以对这些对象有引用。这不是将参数传递给函数时发生的特定情况。例如,l3
是一个引用。正是这个引用按值传递给函数,本地变量lb
接收这个作为它自己的值(对象引用被复制)。当那个函数赋值给lb
,那么这只会影响那个局部变量...而不是那个用来把这个值传递给函数的变量。
根据经验:当 JavaScript 函数分配一个值给它的一个参数变量时,这只对那个局部变量有影响1。
但是,当函数改变参数变量的属性时,这将影响作为参数传递的对象。
总之,没有像 C++ 那样的传递引用。 JavaScript 使用按值传递机制,考虑到对象的值实际上是一个引用。
避免代码重用
在你的情况下,l3
的处理应该像你对待carry
一样。就像函数返回重新分配的carry
一样,它也应该返回l3
。您可以通过返回具有这两个值的数组来做到这一点。然后您可以在调用方使用解构赋值。老实说,这看起来并不那么优雅,但是有一种不同的方法可以避免代码重复:
请考虑第二个和第三个while
循环都会进行迭代的情况永远不会发生。这是因为在第一个循环结束后,l1
和 l2
中的一个最多可以是非 null
。
因此,抽象出l1
和l2
之间的这种区别,并仅使用一个循环来替换这两个循环:
let lrest = l1 || l2; // This will select the non-null list if there is one
while (lrest != null)
l3.next = new ListNode(carry, null);
l3 = l3.next;
let sum = lrest.val + l3.val;
let ones = sum % 10;
carry = Math.floor(sum / 10);
l3.val = ones;
lrest = lrest.next;
1此规则存在罕见的例外情况。有一些别名行为,例如 arguments
在非严格模式下的工作方式。
【讨论】:
以上是关于为啥我的链表代码在模块化可重用 JavaScript 函数中不起作用?的主要内容,如果未能解决你的问题,请参考以下文章
将第一个节点添加到hashmap中的链表时,为啥必须将新节点直接分配给索引指针?