我正在研究Javascript中的数据结构以准备面试,并且对解决方案中的返回值有点束手无策。详情如下。
问题(参考链接):https://www.hackerrank.com/challenges/delete-a-node-from-a-linked-list/problem
删除链表中给定位置的节点并返回对头部节点的引用。头部位于位置0。删除节点后列表可能为空。在这种情况下,返回空值。
样板:
const SinglyLinkedListNode = class {
constructor(nodeData) {
this.data = nodeData;
this.next = null;
}
};
const SinglyLinkedList = class {
constructor() {
this.head = null;
this.tail = null;
}
insertNode(nodeData) {
const node = new SinglyLinkedListNode(nodeData);
if (this.head == null) {
this.head = node;
} else {
this.tail.next = node;
}
this.tail = node;
}
};
function printSinglyLinkedList(node, sep, ws) {
while (node != null) {
ws.write(String(node.data));
node = node.next;
if (node != null) {
ws.write(sep);
}
}
}
功能答案是:
/*
* Complete the 'deleteNode' function below.
*
* The function is expected to return an INTEGER_SINGLY_LINKED_LIST.
* The function accepts following parameters:
* 1. INTEGER_SINGLY_LINKED_LIST llist
* 2. INTEGER position
*/
/*
* For your reference:
*
* SinglyLinkedListNode {
* int data;
* SinglyLinkedListNode next;
* }
*
*/
function deleteNode(llist, position) {
let head = llist
let currentNode = head
let count = 1
if (head === null) return;
if (position === 0){
head = head.next
return head
if (currentNode === null || currentNode.next === null) return;
while (count < position){
count++
currentNode = currentNode.next
}
currentNode.next = currentNode.next.next
return head
}
此功能似乎通过了提供的所有测试。
我的问题是:为什么返回“head”会给我正确的答案?在函数中似乎没有改变head。返回列表也会给出正确的答案。
我已经尝试返回当前节点,但显然这提供了来自当前节点的值。
为什么返回“头”会给我正确的答案?
因为head
是对一个节点的引用。只是因为该节点本身也有对下一个节点的引用,并且下一个节点也可能有对下一个节点的引用,所以这个head
引用暗示了一个链表。即使head
引用的节点可能仍然是同一个节点,沿着next
引用链的某个地方,可能会发生变化(并且将会发生变化),因此head
引用隐含地导致一个不再相同的链表。
头部的功能似乎没有改变。
除非需要移除的是head节点本身,否则这是一个真实的陈述。那是因为head
只引用了第一个节点。所以如果第一个节点必须留在链表中,head
不需要改变。会改变的是沿途某个地方的next
引用。
返回llist
也会给出正确的答案。
是的,变量head
的引入实际上是矫枉过正。我们可以在没有那个额外变量的情况下解决这个问题,只需使用llist
,其中这段代码使用head
。
这可能有助于想象这一点。
假设我们有一个包含四个节点(值为1、2、3和4)的链表,并且位置
为2。然后当代码执行到达while
语句时,我们有这样的状态:
llist
head currentNode
↓ ↓
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
│ value: 1 │ │ value: 2 │ │ value: 3 │ │ value: 4 │
│ next: ───────> │ next: ───────> │ next: ───────> │ next:None │
└───────────┘ └───────────┘ └───────────┘ └───────────┘
由于位置
为2,因此此代码的目的是从该列表中删除值为3的节点。
在这种情况下,while
循环将进行一次迭代,以便当前节点
将引用需要删除的节点的前身:
llist
head currentNode
↓ ↓
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
│ value: 1 │ │ value: 2 │ │ value: 3 │ │ value: 4 │
│ next: ───────> │ next: ───────> │ next: ───────> │ next:None │
└───────────┘ └───────────┘ └───────────┘ └───────────┘
while循环之后的第一条语句对存储在该前置节点中的next
引用执行“重定向”:
llist
head currentNode
↓ ↓ ┌────────────────┐
┌───────────┐ ┌───────────┐ │ ┌───────────┐ │ ┌───────────┐
│ value: 1 │ │ value: 2 │ │ │ value: 3 │ └> │ value: 4 │
│ next: ───────> │ next: ──────┘ │ next: ───────> │ next:None │
└───────────┘ └───────────┘ └───────────┘ └───────────┘
请注意,不再有任何引用值为3的节点的内容。任何JavaScript代码都无法访问它。垃圾收集器可以释放它的内存,所以上面的情况与此没有什么不同:
llist
head currentNode
↓ ↓
┌───────────┐ ┌───────────┐ ┌───────────┐
│ value: 1 │ │ value: 2 │ │ value: 4 │
│ next: ───────> │ next: ───────> │ next:None │
└───────────┘ └───────────┘ └───────────┘
在所有这些过程中,head
没有改变。变化反映在下一个
引用链中的一个节点上,所以我们可以说链表发生了突变。
我希望这澄清了它是如何工作的。