二叉搜索树和单链表相关OJ题

1.二叉搜索树转成单链表

对应letecode链接:

力扣

题目描述:

给你二叉树的根结点 root ,请你将它展开为一个单链表:

展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。

示例 1:
输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]
示例 2:

输入:root = []
输出:[]
示例 3:

输入:root = [0]
输出:[0]

提示:

树中结点数在范围 [0, 2000] 内
-100 <= Node.val <= 100

进阶:你可以使用原地算法(O(1) 额外空间)展开这棵树吗?

 解题思路:

利用二叉树前序遍历的非递归我们只需要定义一个变量prev记录前一个节点在将节点之间链接起来即可如果不是很明白二叉树的非递归遍历的老铁可以看一下我的二叉树基础博客

2.gif

 在遍历的过程中把节点链接起来即可

对应代码:

class Solution {
public:
    void flatten(TreeNode* root) {
             if(!root)return;
             TreeNode*prev=nullptr;//记录前一个节点
               stack<TreeNode*>stk;
               stk.push(root);
            while(!stk.empty()){
                  auto node=stk.top();
                  stk.pop();
                  if(prev!=nullptr){
                      prev->right=node;
                      prev->left=nullptr;
                  }
                  if(node->right){//右不为空则入右
                      stk.push(node->right);
                  }
                  if(node->left){//左不为空则入左
                      stk.push(node->left);
                  }
                  prev=node;//记录前一个节点

            }
            
    }
};

方法二:

利用后序遍历:

前序遍历是:打印根节点-左节点-右节点 这样的顺序,如果是:右节点-左节点-打印根节点 这样完全相反的顺序遍历呢
这样遍历完之后,正好跟前序遍历是相反的。
前序遍历完是1,2,3,4,5,6,按照这种新的方式遍历其结果是:6,5,4,3,2,1。
既然得到了反向的顺序,那么就可以把前后节点串联起来,当遍历到根节点1的时候,整个串联就完成了,二叉树就变成了链表。
null<-6<-5<-4<-3<-2<-1

4.gif

对应代码:

class Solution {
public:
TreeNode*last=nullptr;
    void flatten(TreeNode* root) {
        if(root==nullptr){
            return;
        }
        flatten(root->right);
         flatten(root->left);
         root->right=last;
         root->left=nullptr;
         last=root;
    }
};

将有序数组转成二叉搜索树

对应letecode链接:

108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode) (leetcode-cn.com)

题目描述:

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。

高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。

示例 1:
输入:nums = [-10,-3,0,5,9]
输出:[0,-3,9,-10,null,5]
解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:

示例 2:
输入:nums = [1,3]
输出:[3,1]
解释:[1,3] 和 [3,1] 都是高度平衡二叉搜索树。

提示:

1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 按 严格递增 顺序排列

 解题思路:

二叉搜索树(Binary Search Tree)是指一棵空树或具有如下性质的二叉树:

1.若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值
若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值
任意节点的左、右子树也分别为二叉搜索树
没有键值相等的节点
基于以上性质,我们可以得出一个二叉搜索树的特性:二叉搜索树的中序遍历结果为递增序列。

那么现在题目给了我们一个递增序列,要求我们构造一棵二叉搜索树,就是要我们实现这一特性的逆过程。

还记得什么是中序遍历吗?中序遍历的顺序为:左节点 to→ 根节点 to→ 右节点。这个遍历过程可以使用递归非常直观地进行表示。

 如何构造这棵树呢?

  1. 选取节点
  2. 构造该节点的左子树
  3. 构造该节点的右子树

 由于题目要我们构造的是平衡搜索二叉树,所以我们可以使用数组的中点对应的值作为根节点的值来保证他的平衡性

以题目给出的 [-10,-3,0,5,9] 为例。

我们选取数组中点,即数字 0 作为根节点。此时,以 0 为分界点将数组分为左右两个部分,左侧为 [-10, -3],右侧为 [5, 9]。因该数组为升序排列的有序数组,所以左侧数组值均小于 0,可作为节点 0 的左子树;右侧数组值均大于 0,可作为节点 0 的右子树。

 

重复上述步骤,将 [-10, -3] 和 [5, 9] 单独看作两棵树,从而继续为他们构造左右子树。

对于左侧数组 [-10, -3],我们选取 -3 作为根节点;对于右侧数组 [5, 9],选取 9 作为根节点。

最终构造结果如下:

对应代码:

TreeNode*buildTree(vector<int>&nums,int left,int right)
 {
     if(left>right)//区间不存在则返回nullptr
     {
         return nullptr;
     }
     int mid=(left+right)>>1;//获取数组中间位置的下标
      TreeNode*root=new TreeNode(nums[mid]);
      root->left=buildTree(nums,left,mid-1);//构造左树
      root->right=buildTree(nums,mid+1,right);//构造右树
      return root;//返回根节点
 }
class Solution {
public:

    TreeNode* sortedArrayToBST(vector<int>& nums) {
        return buildTree(nums,0,nums.size()-1);
    }
};

有序链表转换为二叉搜索树

对应letecode链接:

109. 有序链表转换二叉搜索树 - 力扣(LeetCode) (leetcode-cn.com)

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

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

示例:

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

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

      0
     / \
   -3   9
   /   /
 -10  5

解题思路:和上面的类似找到链表的中间节点在将其划分为两部分依次创建即可

image.png

对应代码:

class Solution {
public:
    TreeNode* sortedListToBST(ListNode* head) {
                  if(!head)return nullptr;

                if(!head->next)return new TreeNode(head->val);
                ListNode*fast=head;
                ListNode*slow=head;
                ListNode*prev=nullptr;
                while(fast&&fast->next){//寻找中间节点
                    prev=slow;
                    fast=fast->next->next;
                    slow=slow->next;
                }
                prev->next=nullptr;
                TreeNode*root=new TreeNode(slow->val);//构建左子树
                root->left=sortedListToBST(head);
                root->right=sortedListToBST(slow->next);//构建右子树
                     slow=slow->next;
                return root;//返回根节点
    }
};

二叉搜索树和双向链表

对应letecode链接:

https://leetcode-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof/

题目描述:

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。

为了让您更好地理解问题,以下面的二叉搜索树为例:

我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。

下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。

解题思路:

这道题和上面那几道题思路差不多在这里我们可以利用二叉搜索树的中序遍历是有序的因此我们可以定义一个变量head记录双向循环链表的头 定义一个变量lastvisit前一个访问的节点在连接即可

 对应代码:

class Solution {
public: 
               Node*head=nullptr;//记录头节点
               Node*lastvisit=nullptr;//记录最近访问的节点
    Node* treeToDoublyList(Node* root) {
        if(!root)return nullptr;
        helper(root);
        head->left=lastvisit;
        lastvisit->right=head;
        return head;
    }
    void helper(Node*root){
        if(!root)return;
        helper(root->left);
        if(lastvisit!=nullptr){
            lastvisit->right=root;
        }
        else{
            head=root;//头节点
        }
        root->left=lastvisit;//链接
        lastvisit=root;
        helper(root->right);
        
    }
};

迭代写法:

class Solution {
public:
    Node* treeToDoublyList(Node* root) {
        if(!root)return root;
        Node*lastvisit=nullptr;
        Node*head=nullptr;
        stack<Node*>stk;
        Node*cur=root;
        while(!stk.empty()||cur){
            while(cur){
                stk.push(cur);
                cur=cur->left;
            }
            cur=stk.top();
            stk.pop();
            if(lastvisit!=nullptr){
                lastvisit->right=cur;
            }
            else{
                head=cur;
            }
            cur->left=lastvisit;
            lastvisit=cur;
            cur=cur->right;
        }

        head->left=lastvisit;
        lastvisit->right=head;
        return head;
    }
};

 最后:

🙌🙌🙌🙌
结语:对于个人来讲,在leetcode上进行探索以及单人闯关是一件有趣的时间,一个程序员,如果不喜欢编程,那么可能就失去了这份职业的乐趣。刷到我的文章的人,我希望你们可以驻足一小会,忙里偷闲的阅读一下我的文章,可能文章的内容对你来说很简单,(^▽^)不过文章中的每一个字都是我认真专注的见证!希望您看完之后,若是能帮到您,劳烦请您简单动动手指鼓励我,我必回报更大的付出~ 

 

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个追梦的少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值