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

146 阅读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]
既然链表中不知道右边界的位置,我们可以让两个指针 fastslow 都从 left 出发,slow 每走一步,fast 就走两步,直到 fast 走到了链表的尽头。
因为当 fast 走完链表时,slow 走的距离是 fast 的一半,故 slow 此时指向的是链表的中间位置

  1. slowfast 指向链表的最左边,即头部

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

三、总结

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

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