从C语言到C++_25(树的十道OJ题)力扣:606+102+107+236+426+105+106+144+94+145(中)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 从C语言到C++_25(树的十道OJ题)力扣:606+102+107+236+426+105+106+144+94+145

从C语言到C++_25(树的十道OJ题)力扣:606+102+107+236+426+105+106+144+94+145(上):https://developer.aliyun.com/article/1521948


解析代码:(法一)

class Solution {
public:
    bool Find(TreeNode* sub,TreeNode* x)
    {
        if(sub == nullptr)
        {
            return false;
        }
        return sub == x 
        || Find(sub->left,x) 
        || Find(sub->right,x);
    }
 
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == p || root == q) // 只要其中一个是自己自己,自己就是公共祖先
        {
            return root;
        }
        bool pInLeft,pInRight,qInLeft,qInRight;
        pInLeft = Find(root->left,p);
        pInRight = !pInLeft; // 依题意,p不在左边就在右边
 
        qInLeft = Find(root->left,q);
        qInRight = !qInLeft; // 同上
 
        if((pInLeft && qInRight) || (qInLeft && pInRight))
        {
            return root; // 如果一个在左边,一个在右边,自己就是祖先
        }
        else if(pInLeft && qInLeft) // 如果两个都在左边,递归去左边
        {
            return lowestCommonAncestor(root->left,p,q);
        }
        else // 只剩两个都在右边的情况
        {
            return lowestCommonAncestor(root->right,p,q);
        }
    }
};

法一的时间复杂度是O(H*N)H是树的高度,N是树的结点数,怎么优化到O(N)?

解析代码:(法二)

我们把要找的结点的所有祖先都用栈存起来,

(按前序找,不确定是不是就入栈,确定不是就pop掉继续找)

然后转化为链表相交问题:

class Solution {
public:
    bool FindPath(TreeNode* root,TreeNode* x,stack<TreeNode*>& path)
    {
        if(root == nullptr)
            return false;
 
        path.push(root); // 不管是不是,先入栈
        if(root == x)
            return true; // 找到返回true
        if(FindPath(root->left,x,path))
            return true; // 左树找到返回true
        if(FindPath(root->right,x,path))
            return true; // 右树找到返回true
        path.pop(); // 都找不到
        return false;
    }
    
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        stack<TreeNode*> pPath,qPath;
        FindPath(root,p,pPath);
        FindPath(root,q,qPath);
 
        while(pPath.size() != qPath.size()) // 类似链表相交
        {
            if(pPath.size() > qPath.size())
            {
                pPath.pop();
            }
            else
            {
                qPath.pop();
            }
        }
        while(pPath.top() != qPath.top())
        {
            pPath.pop();
            qPath.pop();
        }
        return pPath.top(); // 相等,返回其中一个
    }
};

剑指 Offer 36. 二叉搜索树与双向链表 - 力扣(LeetCode)

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

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

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

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

特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。

注意:本题与主站 426 题相同:力扣

注意:此题对比原题有改动。

(牛客一道差不多的题的链接:)

二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com)

解析代码:(牛客)

/*
struct TreeNode {
  int val;
  struct TreeNode *left;
  struct TreeNode *right;
  TreeNode(int x) :
      val(x), left(NULL), right(NULL) {
  }
};*/
class Solution {
public:
    void InOrderConvert(TreeNode* cur, TreeNode*& prev) // 希望就有一个prev,传引用
  {
    if(cur == nullptr)
    {
      return;
    }
    InOrderConvert(cur->left,prev);
    cur->left = prev; // 走到中序这,知道cur的left和prev的right
    if(prev)
    {
      prev->right = cur;
    }
    prev = cur; // 往后走
    InOrderConvert(cur->right,prev);
  } 
 
    TreeNode* Convert(TreeNode* pRootOfTree) {
        TreeNode* prev = nullptr;
    InOrderConvert(pRootOfTree,prev);
 
    TreeNode* head = pRootOfTree;
    while(head && head->left) // 找链表的头结点
    {
      head = head->left;
    }
    return head;
    }
};

解析代码:(力扣)

力扣的就是在牛客的基础上改成双向链表:(要判空了)

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;
    Node() {}
    Node(int _val) {
        val = _val;
        left = NULL;
        right = NULL;
    }
    Node(int _val, Node* _left, Node* _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};
*/
class Solution {
public:
    void treeToDoublyListInOrder(Node* cur,Node*& prev)
    {
        if(cur == nullptr)
    {
      return;
    }
    treeToDoublyListInOrder(cur->left,prev);
    cur->left = prev; // 走到中序这,知道cur的left和prev的right
    if(prev)
    {
      prev->right = cur;
    }
    prev = cur; // 往后走
    treeToDoublyListInOrder(cur->right,prev);
    }
    Node* treeToDoublyList(Node* root) {
        if(root == nullptr)
        {
            return nullptr;
        }
 
        Node* prev = nullptr;
        treeToDoublyListInOrder(root,prev);
 
        Node* head = root;
        while(head && head->left) // 找链表的头结点
        {
            head = head->left;
        }
        Node* tail = root; // 变成双向循环链表
        while(tail && tail->right)
        {
            tail = tail->right;
        }
        head->left = tail;
        tail->right = head;
        return head;
    }
};

105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

难度中等

给定两个整数数组 preorderinorder ,其中 preorder 是二叉树的先序遍历inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。


示例 1:

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]

输出: [3,9,20,null,null,15,7]

示例 2:

输入: preorder = [-1], inorder = [-1]

输出: [-1]

提示:

  • 1 <= preorder.length <= 3000
  • inorder.length == preorder.length
  • -3000 <= preorder[i], inorder[i] <= 3000
  • preorderinorder无重复 元素
  • inorder 均出现在 preorder
  • preorder 保证 为二叉树的前序遍历序列
  • inorder 保证 为二叉树的中序遍历序列
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
 
    }
};

解析代码:

class Solution {
public:
    TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& prei, int left, int right)
  {
    if (left > right)
    {
      return nullptr;
    }
    TreeNode* root = new TreeNode(preorder[prei++]); // 先构建好根
    int ini = left; // 分割中序
    while (ini <= right)
    {
      if (inorder[ini] == root->val)
      {
        break;
      }
      else
      {
        ini++;
      }
    }
    // [left,ini-1] ini [ini+1,right] 构建好根之后,构建根的左右子树
        root->left = _buildTree(preorder, inorder, prei, left, ini - 1);
    root->right = _buildTree(preorder, inorder, prei, ini + 1, right);
    return root;
  }
 
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int i = 0;
    return _buildTree(preorder, inorder, i, 0, inorder.size() - 1);
    }
};

106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

难度中等

给定两个整数数组 inorderpostorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树

示例 1:

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]

输出:[3,9,20,null,null,15,7]


示例 2:

输入:inorder = [-1], postorder = [-1]

输出:[-1]


提示:

  • 1 <= inorder.length <= 3000
  • postorder.length == inorder.length
  • -3000 <= inorder[i], postorder[i] <= 3000
  • inorderpostorder 都由 不同 的值组成
  • postorder 中每一个值都在 inorder
  • inorder 保证是树的中序遍历
  • postorder 保证是树的后序遍历
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
 
    }
};

解析代码:

class Solution {
public:
  TreeNode* _buildTree(vector<int>& inorder, vector<int>& postorder, int& prei, int left, int right)
  {
    if (left > right)
    {
      return nullptr;
    }
    TreeNode* root = new TreeNode(postorder[prei--]); // 从后面开始new
    int ini = left; // 分割中序
    while (ini <= right)
    {
      if (inorder[ini] == root->val)
      {
        break;
      }
      else
      {
        ini++;
      }
    }
    // [left,ini-1] ini [ini+1,right] 先构建右再构建左
    root->right = _buildTree(inorder, postorder, prei, ini + 1, right);
        root->left = _buildTree(inorder, postorder, prei, left, ini - 1);
    return root;
  }
 
  TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
    int i = inorder.size() - 1;
    return _buildTree(inorder, postorder, i, 0, inorder.size() - 1);
  }
};

从C语言到C++_25(树的十道OJ题)力扣:606+102+107+236+426+105+106+144+94+145(下):https://developer.aliyun.com/article/1521948?spm=a2c6h.13148508.setting.19.50c04f0eHmAr4x

目录
相关文章
|
2月前
|
安全 编译器 C语言
C++入门1——从C语言到C++的过渡
C++入门1——从C语言到C++的过渡
68 2
|
21天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
43 5
|
25天前
|
存储 C++
【C++】AVL树
AVL树是一种自平衡二叉搜索树,由Georgy Adelson-Velsky和Evgenii Landis提出。它通过确保任意节点的两子树高度差不超过1来维持平衡,支持高效插入、删除和查找操作,时间复杂度为O(log n)。AVL树通过四种旋转操作(左旋、右旋、左-右旋、右-左旋)来恢复树的平衡状态,适用于需要频繁进行数据操作的场景。
33 2
|
1月前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
74 16
|
18天前
|
算法 编译器 C语言
【C语言】C++ 和 C 的优缺点是什么?
C 和 C++ 是两种强大的编程语言,各有其优缺点。C 语言以其高效性、底层控制和简洁性广泛应用于系统编程和嵌入式系统。C++ 在 C 语言的基础上引入了面向对象编程、模板编程和丰富的标准库,使其适合开发大型、复杂的软件系统。 在选择使用 C 还是 C++ 时,开发者需要根据项目的需求、语言的特性以及团队的技术栈来做出决策。无论是 C 语言还是 C++,了解其优缺点和适用场景能够帮助开发者在实际开发中做出更明智的选择,从而更好地应对挑战,实现项目目标。
43 0
|
2月前
|
C语言 C++
C 语言的关键字 static 和 C++ 的关键字 static 有什么区别
在C语言中,`static`关键字主要用于变量声明,使得该变量的作用域被限制在其被声明的函数内部,且在整个程序运行期间保留其值。而在C++中,除了继承了C的特性外,`static`还可以用于类成员,使该成员被所有类实例共享,同时在类外进行初始化。这使得C++中的`static`具有更广泛的应用场景,不仅限于控制变量的作用域和生存期。
67 10
|
3月前
|
算法 机器人 C语言
ROS仿真支持C++和C语言
ROS仿真支持C++和C语言
91 1
|
2月前
|
C语言 C++
实现两个变量值的互换[C语言和C++的区别]
实现两个变量值的互换[C语言和C++的区别]
26 0
|
17天前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
39 10
|
17天前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
41 9