写两个帮助函数来建立链表。系列目录见 前言和目录 。javascript
写两个方法 push
和 buildList
来初始化链表。尝试在 buildList
中使用 push
。下面的例子中我用 a -> b -> c
来表示链表,这是为了书写方便,并非 JavaScript 的有效语法。java
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
。这个方法应该始终返回一个新的链表。node
buildList
接收一个数组为参数,建立对应的链表。git
buildList([1, 2, 3]) === 1 -> 2 -> 3 -> null
做为链表系列的第一课,咱们须要先定义节点对象是什么样子。按照 Codewars 上的设定,一个节点对象有两个属性 data 和 next 。data 是这个节点的值,next 是下一个节点的引用。这是默认的类模板。github
function Node(data) { this.data = data this.next = null }
这是 push
的基本实现:segmentfault
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) }
这个函数很是适合用递归实现。这是递归的版本:测试
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])
的返回值。以此类推:ui
第一轮 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 }
结合循环版本的思路和 JavaScript 的数组迭代器,咱们能够得出一个 one-liner 版本。
function buildListV3(array) { return (array || []).reduceRight(push, null) }
这个就不解释了,留给各位本身思考下吧。