相比于LeetCode 108 , 这道题变成了中等难度的题目, 在 107 中, 我使用的是以中心点作为分割, 然后数组左右两部分分别作为左子树和右子树去进行递归, 这道题我也打算使用这个思路, 步骤为:
1. 找到链表的中心位置.
2. 构造子树
3. 递归
问题在于如何找到链表的中心节点?
LeetCode 前面的有关于链表的题目, 经常是会使用"快慢指针"的方法:
快指针一次步进2个节点, 慢指针一次步进1个节点,这样当快指针到达终点的时候,慢指针所指向的节点就是中间的节点.
题目假设的是, 升序链表. 也就意味着不可能有环? 严格来说, 所有元素相同的情况下,构成一个环, 也可以算是"升序链表"吧,看起来有点不严谨, 不过不深究了. 最好还是 快慢指针 也可以同时检查有没有环.
最后写出来的算法是这样的
def sortedListToBST(head: ListNode): TreeNode = {
def middle(from:ListNode, until: ListNode):ListNode = {
if(from == null || (from eq until)) null
else {
var i = from
var j = from
var end = false
while(!(j.next eq until) && !end) {
i = i.next
j = j.next
if(j != null && !(j.next eq until)){
j = j.next
} else end = true
}
i
}
}
def buildTree(from: ListNode, until: ListNode): TreeNode = {
if(from == null) null
else if(from.next eq until) new TreeNode(from.x)
else {
val middleNode = middle(from, until)
if(middleNode == null) {
null
} else {
val tNode = new TreeNode(middleNode.x)
tNode.left = buildTree(from,middleNode)//使用 until的好处在于此, 左半边不需要知道上一个节点
tNode.right = buildTree(middleNode.next,until)
tNode
}
}
}
if(head == null) null
else buildTree(head, null)
}
居然没有我大Scala的语法高亮, ╭(╯^╰)╮
其中middle函数是用来计算中间节点的
buildTree函数用来进行递归
平常的递归实现的话, 我比较喜欢使用 from, to 的下标, 后者是包含在内部的, 总觉得思考起来比较方便, 但是这次的话,使用until会比较好, 因为使用 to 的话, 还需要找到上一个节点, 这里不方便.
虽然这个算法可以通过, 但是不是很高效, 因为每次都要遍历去寻找中心节点, 相当于应该遍历 n( 1 + 1/2 + 1/4 + ...) 次(不超过2次)
但是思想却很朴素, 递归思想, 不搞花里胡哨的, 看起来还是美的呀.