「这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战」
前言
力扣第109题 有序链表转换二叉搜索树
如下所示:
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定的有序链表: [-10, -3, 0, 5, 9],
一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
一、思路
这一题与上一题力扣第108题-将有序数组转换为二叉搜索树基本是非常相似的,唯一不同的是将 升序数组
换成了 升序的链表
。
因为要保证左右两个子树的高度差的绝对值不超过 1,所以在 升序数组
中每次根节点取的都是中间位置的值作为根节点。但是链表中无法直接获取到中间位置的值,所以我一开始是遍历了整个链表,将链表转为了列表 ArrayList
。
解法一:先转为 List
- 遍历链表,并转为
List
- 每次取当前区间内
left ~ right
的中间值作为根节点
伪代码如下所示:
public TreeNode sortedListToBST(ListNode head) {
List<Integer> list = linkList2ArrayList(head); // 转为List
return dfs(list, 0, list.size()-1);
}
public TreeNode dfs(List<Integer> list, int left, int right){
if (left > right)
return null;
int mid = (left+right)/2; // 取中间值
TreeNode root = new TreeNode(list.get(mid));
root.left = dfs(list, left, mid-1);
root.right = dfs(list, mid+1, right);
return root;
}
我用这种方式实现后,发现击败率比较低(约为
28%
)。于是我就去看了下官方的题解,发现官方使用的是 快慢指针 来找到中间位置,从而避免了遍历整个链表。
解法二:快慢指针
我们先用图解的方式说一下,如何使用快慢指针找到链表中的中间位置。
假设有一个链表 1-> 2 -> 3 -> 4 -> 5 -> 6 -> 7
,你如何找到链表的中间元素 4
呢?
对于数组来说,由于知道数组的
left
和数组的right
(这里right指的是arr.lenght
)。所以中间位置就是mid = (left+right)/2
,中间元素就为arr[mid]
。
既然链表中不知道右边界的位置,我们可以让两个指针fast
和slow
都从left
出发,slow
每走一步,fast
就走两步,直到fast
走到了链表的尽头。
因为当fast
走完链表时,slow
走的距离是fast
的一半,故slow
此时指向的是链表的中间位置
slow
和fast
指向链表的最左边,即头部
fast
走两步,slow
走一步
fast
再走两步,slow
走一步
fast
再走两步,slow
走一步。此时fast
到头了,slow
指向的就是中间元素4
经过上面的图解,我们就知道了如何在链表中找到中间元素了。那剩下的步骤也就不难了。我们只需要:更新链表的左右边界,在左右边界中找到中间位置作为根节点即可
二、实现
实现代码
这里主要贴一下解法二的实现代码,注意刚开始的 right=null
public TreeNode sortedListToBST(ListNode head) {
return buildTree(head, null);
}
public TreeNode buildTree(ListNode left, ListNode right) {
if (left == right) {
return null;
}
ListNode mid = getMid(left, right);
TreeNode root = new TreeNode(mid.val);
root.left = buildTree(left, mid);
root.right = buildTree(mid.next, right);
return root;
}
/**
* 快慢指针
*/
public ListNode getMid(ListNode left, ListNode right) {
ListNode fast = left;
ListNode slow = left;
while (fast != right && fast.next != right) {
fast = fast.next;
fast = fast.next;
slow = slow.next;
}
return slow;
}
测试代码
public static void main(String[] args) {
ListNode listNode = new ListNode(-10, new ListNode(-3, new ListNode(0, new ListNode(5, new ListNode(9)))));
TreeNode treeNode = new Number109().sortedListToBST(listNode);
System.out.println("debug");
}
结果
三、总结
感谢看到最后,非常荣幸能够帮助到你~♥
如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~