题目描述:给出一个所有元素以升序排序的单链表,将它转换成一棵高度平衡的二分查找树
我们之前做过一道将排序数组转换为二分查找树的问题,详见:点击打开链接
将链表转换为二分查找树与上面这道题的基本思想一模一样(在这里,高度平衡,也就是高度最小),所以我们要解决的问题就是查找到排序链表的中值,作为根节点,然后将链表分割,分别按照同样的思想生成左右子树。(如果对这个算法有疑问,请先搞懂我刚才给出的链接的那道题)
那么如何查找链表的中值呢?当然不能全遍历一遍,再回溯。这方法效率太低,我在这里给出一种通用的“快慢指针法”
我假设看我这篇文章的人对链表的基本结构已经很熟悉了。那么现在可以设置两个指针slow和fast,我们令这两个指针一开始都指向链表的头结点--head,然后slow每次指向链表的下一位(就是它现在所指向的节点的next),而fast每次指向他的下一位的下一位(也就是每次向后移动两个节点),那当fast指向尾节点的时候,slow指向的刚好是中间节点。
举个例子:1->2->3->4->5
1. 一开始,slow,fast都指向1
2. slow移动一位,指2;fast移动两位,指3
3. slow接着移动一位,指3;fast接着移动两位,指5. 此时fast指向尾节点,slow刚好指向中间节点。
这是快慢指针的基本应用,当然具体处理问题的时候,快慢指针会根据具体问题做相应修改,所以,一定要灵活运用。
就拿这道题来说吧,按照上面的快慢指针法,当然能找到中间节点,但是,随后我们是要对链表分割的,而只有知道中间节点之前的那个节点,才能实现“摘链”(就是把链表的一部分“摘”下来,形成单独的链表)
所以,此处也有个通用的技巧,就是在链表的头结点之前人为的放置一个dummy节点帮助我们处理问题。还是上面的那个例子,当放置了dummy节点之后,就变成下面这个样子了:
dummy->1->2->3->4->5
可以令slow初始时指向dummy,fast初始时指向head,还是刚才那样扫描链表,发现当fast指向尾节点时,slow刚好指向2,正好在中间节点前面。我们就能实现摘链了。
所以,这个题的思路没什么值得注意的,倒是对链表的操作,是需要学习的。
代码可以给出了:
"""
Definition of ListNode
class ListNode(object):
def __init__(self, val, next=None):
self.val = val
self.next = next
Definition of TreeNode:
class TreeNode:
def __init__(self, val):
self.val = val
self.left, self.right = None, None
"""
class Solution:
"""
@param head: The first node of linked list.
@return: a tree node
"""
def sortedListToBST(self, head):
if head is None:
return None
# 随意赋值给dummy即可
dummy = TreeNode(-1)
# dummy连在head前面
dummy.next = head
slow, fast = dummy, head
# 直到fast指向尾巴
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# 找到中间节点了,为slow.next
root = TreeNode(slow.next.val)
# 以下三行为摘链,需注意操作顺序
second = slow.next.next
slow.next = None
first = dummy.next
# 递归
root.left = self.sortedListToBST(first)
root.right = self.sortedListToBST(second)
return root
# write your code here
链表对于算法设计的意义是什么呢?我个人的理解:链表是树和图的基础,也可以说是一种结构简单的树或者图,而树和图能够形象的刻画现实问题,这为算法的设计提供了广阔的空间。