TL;DR
写两个协助函数来建立链表。系列目次见 前言和目次 。
需求
写两个要领 push
和 buildList
来初始化链表。尝试在 buildList
中运用 push
。下面的例子中我用 a -> b -> c
来示意链表,这是为了誊写轻易,并非 JavaScript 的有用语法。
let chained = null
chained = push(chained, 3)
chained = push(chained, 2)
chained = push(chained, 1)
push(chained, 8) === 8 -> 1 -> 2 -> 3 -> null
push
用于把一个节点插进去到链表的头部。它吸收两个参数 head 和 data ,head 可所以一个节点对象或许 null
。这个要领应当一直返回一个新的链表。
buildList
吸收一个数组为参数,建立对应的链表。
buildList([1, 2, 3]) === 1 -> 2 -> 3 -> null
定义节点对象
作为链表系列的第一课,我们须要先定义节点对象是什么模样。根据 Codewars 上的设定,一个节点对象有两个属性 data 和 next 。data 是这个节点的值,next 是下一个节点的援用。这是默许的类模板。
function Node(data) {
this.data = data
this.next = null
}
push
这是 push
的基础完成:
function push(head, data) {
const node = new Node(data)
if (head) {
node.next = head
return node
} else {
return node
}
}
我更倾向于修正一下 Node 的组织函数,把 next 也当做参数,而且加上默许值,这会让背面的事变简化许多:
function Node(data = null, next = null) {
this.data = data
this.next = next
}
新的 push
完成:
function push(head, data) {
return new Node(head, data)
}
buildList
递归版本
这个函数异常适合用递归完成。这是递归的版本:
function buildList(array) {
if (!array || !array.length) return null
const data = array.shift()
return push(buildList(array), data)
}
递归的思绪是,把大的庞杂的操纵逐渐分解成小的操纵,直到分解成最基础的状况。拿这个例子诠释,给定数组 [1, 2, 3]
,递归的完成思绪是逐渐往链表头部插进去数据 3,2,1 ,一共三轮。第一轮相当于 push(someList, 3)
。这个 someList
是什么呢,实在就是 buildList([1, 2])
的返回值。以此类推:
第一轮
push(buildList([1, 2]), 3)
第二轮
push(buildList([1]), 2)
第三轮
push(buildList([]), 3)
到第三轮就已是最基础的状况了,数组为空,这时候返回 null
代表空节点。
轮回版本
遵照上面的思绪,轮回也很轻易完成,只需反向遍历数组就行。由于轮回已斟酌了数组为空的状况,这里就不必举行边境判断了。
function buildListV2(array) {
let list = null
for (let i = array.length - 1; i >= 0; i--) {
list = push(list, array[i])
}
return list
}
One-liner
连系轮回版本的思绪和 JavaScript 的数组迭代器,我们能够得出一个 one-liner 版本。
function buildListV3(array) {
return (array || []).reduceRight(push, null)
}
这个就不诠释了,留给列位本身思索下吧。