108. 将有序数组转换为二叉搜索树 109. 有序链表转换二叉搜索树 (解法实现和要点)

108:将有序数组转换为二叉搜索树

题目:

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

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

示例:

给定有序数组: [-10,-3,0,5,9],

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

      0
     /  \
   -3   9
   /     /
 -10  5

解法:

class Solution {
private:
    TreeNode* buildTree(vector<int>& nums, int left, int right) {
        if (left > right)                   //left和right的相对大小就可以说明问题
            return NULL;                    //无须left < 0 || left > right || right >= len
        
        int mid = (left + right)>>1;
        TreeNode* node = new TreeNode(nums[mid]);
        node->left = buildTree(nums, left, mid-1);
        node->right = buildTree(nums, mid+1, right);

        return node;
    }
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        int len = nums.size();
        if (len == 0)
            return NULL;

        return buildTree(nums, 0, len-1);  //root的处理和子区间一样,直接调用
    }
};

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

题目:

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

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

示例:

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

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

      0
     /  \
   -3   9
   /    /
 -10  5

本题要点:

1:链表升序

2:高度差不超过1,即需要找出中间节点

解法1: 基于108的实现,可将提升难度的链表结构转化为数组

class Solution {
public:
    TreeNode* sortedListToBST(ListNode* head) {
        vector<int> vec;
        while(head != nullptr){
            vec.push_back(head->val);
            head = head->next;
        }
        return buildTree(vec, 0, vec.size());
    }
    TreeNode* buildTree(vector<int>& v, int begin, int end){
        if(begin == end)
            return nullptr;

        int mid = (begin+end)>>1;
        TreeNode * root = new TreeNode(v[mid]);
        root->left = buildTree(v, begin, mid);
        root->right = buildTree(v, mid+1, end);

        return root;
    }
};

解法2:快慢指针+前序指针找出中间节点前一节点,截断链表

class Solution {
private:
    TreeNode* reverseList(ListNode* head) {
        if (NULL == head)
            return NULL;

        ListNode *slow = head, *fast = head, *pre = head;  //快慢指针找中点
        while (fast && fast->next) {          //pre记录中点前一节点
            pre = slow;
            slow = slow->next;
            fast = fast->next->next;
        }

        TreeNode* node;
        if (pre == slow) {                    //只有一个节点
            node = new TreeNode(slow->val);
            node->left = NULL;
            node->right = NULL;
        } else {
            slow = pre->next;
            pre->next = NULL;                 //head是左链表头节点,slow是中点
            node = new TreeNode(slow->val);
            node->left = reverseList(head);
            node->right = reverseList(slow->next);
        }

        return node;
    }
public:
    TreeNode* sortedListToBST(ListNode* head) {
        return reverseList(head);
    }
};

         这种实现在处理单独节点时显得比较繁琐,可以在返回条件中单独判断只有一个有效节点情况,因为这种情况无法继续截断链表。优化实现如下程序:

class Solution {
private:
    TreeNode* reverseList(ListNode* head) {
        if (NULL == head)
            return NULL;
        if (NULL == head->next)               //有一个节点时,无法拆分为两个链表
            return new TreeNode(head->val);
        
        ListNode *slow = head, *fast = head, *pre = head;  //快慢指针找中点
        while (fast && fast->next) {          //pre记录中点前一节点
            pre = slow;
            slow = slow->next;
            fast = fast->next->next;
        }

        pre->next = NULL; //head是左链表头节点,slow是中点
        TreeNode* node = new TreeNode(slow->val);
        node->left = reverseList(head);
        node->right = reverseList(slow->next);

        return node;
    }
public:
    TreeNode* sortedListToBST(ListNode* head) {
        return reverseList(head);
    }
};

解法3:快慢指针,不截断链表,传链表首尾节点

class Solution {
private:
    TreeNode* buildTree(ListNode* head, ListNode* tail){
        if(head == tail)
            return nullptr;

        ListNode *slow = head, *fast = head;
        while(fast != tail && fast->next != tail){
            slow = slow->next;
            fast = fast->next->next;
        }
        TreeNode *root = new TreeNode(slow->val);
        root->left = buildTree(head, slow);
        root->right = buildTree(slow->next, tail);

        return root;
    }
public:
    TreeNode* sortedListToBST(ListNode* head) {
        return buildTree(head, nullptr);
    }
    
};

        这种思路很巧妙的一点是,把之前的(fast && fast->next)即已经潜意识的判断(fast != NULL && fast->next != NULL),变成(fast != tail && fast->next != tail)。冥冥之中,把很多指针的操作都给简化了。

        此外,估计有部分同学会想到反转一半的链表,但是就是通不过测试例,那是因为链表本身是升序的,若反转链表,则反转后的链表就是降序了。升序和降序的子链表一样的构造步骤,肯定是不对的。为了构造对的二叉搜索树,这道题不能涉及反转链表,只能是一段段地构造,断不断链表是一种方法,或者修改数据结构。如果更好的思路,欢迎一起探讨。加油。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值