leetcode109. 有序链表转换二叉搜索树(Python3、c++)

文章目录

  • leetcode109. 有序链表转换二叉搜索树
    • 方法一:递归分治
      • 思路:
      • 代码:
        • Python3:
        • cpp:
      • 结果:
    • 方法二:中序遍历优化
      • 思路:
      • 代码:
        • Python3:
        • cpp:
      • 结果:

leetcode109. 有序链表转换二叉搜索树

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

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

示例:

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

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

      0
     / \
   -3   9
   /   /
 -10  5

方法一:递归分治

思路:

由题意,需要转换为高度平衡的BST,因此左右的高度差绝对值不超过1。**那么我们的根节点应该为有序链表的中位数。这里的中位数指的是,如果链表长度为奇数,那么就是中间的那个数,如果为偶数,中间的两个之一即可。**这样左子树和右子树的结点数量差最多为1,因此高度是平衡的。

二叉搜索树的左右子树也应该是二叉搜索树,因此,我们使用分治的方法,对左右子树的构建重复上面找中位数的过程。

找中位数的过程,**由于是链表,我们使用快慢指针来完成,快指针每次走两步,慢指针走一步,快指针走到头的时候,慢指针对应的即为中位数。**搜索的范围我们定位左开右闭[left,right),这样做的目的也是为了考虑到链表。最开始的时候搜索范围是[head,None),对于[left,right),假设找到的中位数是mid,那么root值即为mid的值,左子树的搜索范围即为[left,mid),右子树的搜索范围即为[mid.next,right)。

通过上面的递归分治过程,逐渐完成二叉树的建立。

代码:

Python3:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def sortedListToBST(self, head: ListNode) -> TreeNode:
        # 快慢指针找到搜索链表范围的中位数结点返回
        def getmid(left:ListNode,right:ListNode) -> ListNode:
            slow = left
            fast = left
            while fast != right and fast.next != right:
                fast = fast.next.next
                slow = slow.next
            return slow
        # 构建BST 
        def build(left:ListNode,right:ListNode) -> TreeNode:
            if left == right:
                return None
            mid = getmid(left,right)
            root = TreeNode(mid.val)
            root.left = build(left,mid)
            root.right = build(mid.next,right)
            return root
        # 返回构建BST之后的根节点
        return build(head,None)

cpp:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* sortedListToBST(ListNode* head) {
        return build(head,nullptr);
        
    }
    ListNode* getmid(ListNode* left,ListNode* right){
        ListNode* slow = left;
        ListNode* fast = left;
        while (fast != right && fast->next!=right){
            fast = fast->next->next;
            slow = slow->next;
        }
        return slow;
    }
    TreeNode* build(ListNode*left,ListNode*right){
        if (left == right) return nullptr;
        auto mid = getmid(left,right);
        TreeNode* root = new TreeNode(mid->val);
        root->left = build(left,mid);
        root->right = build(mid->next,right);
        return root;
    }
};

结果:

leetcode109. 有序链表转换二叉搜索树(Python3、c++)_第1张图片

leetcode109. 有序链表转换二叉搜索树(Python3、c++)_第2张图片

方法二:中序遍历优化

思路:

上面的方法中,需要重复调用getmid函数,时间复杂度比较高。我们还可以利用另外一条性质进行优化,即二叉搜索树的中序遍历是递增的序列,因此链表即为二叉搜索树的中序遍历。

因此,我们跟上面的一致,同样找中位数作为root,但是不同的在于,我们先不赋值,先占位,即没有val的结点。完成构建平衡二叉树的同时中序遍历,使用链表中的值按顺序填充每个节点的值,这样就降低了时间复杂度。

在进行之前,需要遍历一遍链表,计算出一共有多少个节点,**将节点编号为[0,n),左开右闭,与上面方法一致,进行建树的过程。找到mid之后,左右子树就是[0,mid)和[mid+1,n)。**中序遍历的同时,中序遍历的第一个点即为最小值,即是head的值,然后head.next直到建树结束,中序遍历也结束了。

注意head应该是全局变量,在建树的时候它只变一次,从头走到尾,因此递归中,对于python,应该加nonlocal关键字限制;对于cpp,送入的head应该是对head指针的引用。

代码:

Python3:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def sortedListToBST(self, head: ListNode) -> TreeNode:
        # 求链表长度,有多少个节点
        def getlength(head:ListNode) -> int:
            cnt = 0
            while head:
                cnt += 1
                head = head.next
            return cnt
        # 构建BST 
        def build(left:int,right:int) -> TreeNode:
            # 左右相等,返回none
            if left == right:
                return None
            # 中位数,分割左右子树
            mid = (left+right)//2;
            # root先占位
            root = TreeNode()
            # 标准中序遍历过程,先左子树,根节点,最后右子树
            root.left = build(left,mid)
            # head的值填写占位的值
            nonlocal head
            root.val = head.val
            head = head.next
            root.right = build(mid+1,right)
            return root
        # 返回构建BST之后的根节点
        length = getlength(head)
        return build(0,length)

cpp:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* sortedListToBST(ListNode* head) {
        auto n = getlength(head);
        return build(0,n,head);
        
    }
    int getlength(ListNode* head){
        int cnt = 0;
        while (head){
            cnt ++;
            head = head->next;
        }
        return cnt;
    }
    TreeNode* build(int left,int right,ListNode*& head){
        if (left == right) return nullptr;
        int mid = (left+right)/2;
        TreeNode* root = new TreeNode();
        root->left = build(left,mid,head);
        root->val = head->val;
        head = head->next;
        root->right = build(mid+1,right,head);
        return root;
    }
};

结果:

leetcode109. 有序链表转换二叉搜索树(Python3、c++)_第3张图片

leetcode109. 有序链表转换二叉搜索树(Python3、c++)_第4张图片

你可能感兴趣的:(Leetcode做题记录,链表,二叉树,leetcode,数据结构,算法)