「前端刷题」109. 有序链表转换二叉搜索树

77 阅读3分钟

「这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战」。

题目

链接:

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树_每个节点 _的左右两个子树的高度差的绝对值不超过 1。

示例:

给定的有序链表: [-10, -3, 0, 5, 9],

一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:

  0
 / \\

-3 9 / / -10 5

思路1, 快慢指针 + 分治递归

  • 首先要确定根节点,因为要构造平衡的二叉树,可以选定链表的中间节点作为根节点,因为这样构造出来的树会尽量平衡
  • 使用快慢指针定位链表中间节点,构建根节点,分治递归构建左右子树

快慢指针:快慢指针同时指向链表头节点,快指针移动2步,慢指针移动1步,直到快指针到达尾节点,此时慢指针指向的节点即为中间节点

代码

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * 
 * 快慢指针 + 递归-分治
 * 
 * @param {ListNode} head
 * @return {TreeNode}
 */
var sortedListToBST = function(head) {
    return buildTree(head, null);
};

var buildTree = function (head, tail) {
    if (!head || head === tail) {
        return null;
    }
    
    // 快慢指针 得到链表中间节点
    let fast = head;
    let slow = head;
    while (fast !== tail && fast.next !== tail) {
        slow = slow.next;
        fast = fast.next.next;
    }

    // 递归构造二叉树
    let val = slow.val;
    let root = new TreeNode(val);

    root.left = buildTree(head, slow);
    root.right = buildTree(slow.next, tail);

    return root;
}

思路2,中序遍历 + 递归

对链表进行中序遍历,还原二叉树

  • 因为给定的链表是有序的,所以二叉树的中序遍历结果是递增的,其实就是链表的值,只不过此时链表少了 null值
  • 链表的头节点是构造出来的树的最左子树的根节点

维护指针 h,从链表头结点开始,用 h.val 构建节点,构建一个,指针后移一位

  • 先求出链表总节点数,每次求出链表的中间位置mid,分治,先根据左链递归构建左子树,
    它会尽可能去分左右链,直到分不下去就返回 null,然后创建最左子树的根节点,
    接上它的两个null,然后 h 指针后移,创建下一个节点

代码

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * 
 * 
 * @param {ListNode} head
 * @return {TreeNode}
 */
var sortedListToBST = function(head) {
    if (head == null) {
        return null;
    }

    // h初始指向头结点
    let h = head; 

    // 链表总节点数
    let len = 0;
    while (head) { 
        len++;
        head = head.next;
    }

    const buildTree = (left, right) => {
        // 递归出口,返回null节点
        if (left > right) {
            return null;    
        }

        // 求mid中间节点
        const mid = left + ((right - left) >> 1);  
        
        const root = new TreeNode();

        // 先递归构建左子树
        const leftNode = buildTree(left, mid - 1); 
        // 构建左子树
        root.left = leftNode; 
        root.val = h.val;
        
        // h指针前进
        h = h.next; 

        // 构建右子树 
        root.right = buildTree(mid + 1, right);

        return root;
    };

    return buildTree(0, len - 1);
};