力扣第109题-有序链表转换二叉搜索树

240 阅读3分钟

「这是我参与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

  1. 遍历链表,并转为 List
  2. 每次取当前区间内 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 呢?

image.png

对于数组来说,由于知道数组的 left 和数组的 right(这里right指的是 arr.lenght)。所以中间位置就是 mid = (left+right)/2,中间元素就为 arr[mid]。
既然链表中不知道右边界的位置,我们可以让两个指针 fast 和 slow 都从 left 出发,slow 每走一步,fast 就走两步,直到 fast 走到了链表的尽头。
因为当 fast 走完链表时,slow 走的距离是 fast 的一半,故 slow 此时指向的是链表的中间位置

  1. slow 和 fast 指向链表的最左边,即头部

image.png

  1. fast 走两步,slow 走一步

image.png

  1. fast 再走两步,slow 走一步

image.png

  1. fast 再走两步,slow 走一步。此时 fast 到头了,slow 指向的就是中间元素 4

image.png

经过上面的图解,我们就知道了如何在链表中找到中间元素了。那剩下的步骤也就不难了。我们只需要:更新链表的左右边界,在左右边界中找到中间位置作为根节点即可

二、实现

实现代码

这里主要贴一下解法二的实现代码,注意刚开始的 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");
    }

结果

image.png

三、总结

感谢看到最后,非常荣幸能够帮助到你~♥

如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~