从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

从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

目录
相关文章
|
6月前
|
安全 C语言 C++
比较C++的内存分配与管理方式new/delete与C语言中的malloc/realloc/calloc/free。
在实用性方面,C++的内存管理方式提供了面向对象的特性,它是处理构造和析构、需要类型安全和异常处理的首选方案。而C语言的内存管理函数适用于简单的内存分配,例如分配原始内存块或复杂性较低的数据结构,没有构造和析构的要求。当从C迁移到C++,或在C++中使用C代码时,了解两种内存管理方式的差异非常重要。
233 26
|
安全 编译器 C语言
C++入门1——从C语言到C++的过渡
C++入门1——从C语言到C++的过渡
268 2
|
7月前
|
Java C++
力扣第一道困难题《3. 无重复字符的最长子串》,c++
首先我们看到这个题是肯定有一种暴力的硬解思路的,那就是将两个vector直接链接起来,然后再排序后,直接返回中间值,这个方法实现起来还是非常容易的,
180 0
|
算法 编译器 C语言
【C语言】C++ 和 C 的优缺点是什么?
C 和 C++ 是两种强大的编程语言,各有其优缺点。C 语言以其高效性、底层控制和简洁性广泛应用于系统编程和嵌入式系统。C++ 在 C 语言的基础上引入了面向对象编程、模板编程和丰富的标准库,使其适合开发大型、复杂的软件系统。 在选择使用 C 还是 C++ 时,开发者需要根据项目的需求、语言的特性以及团队的技术栈来做出决策。无论是 C 语言还是 C++,了解其优缺点和适用场景能够帮助开发者在实际开发中做出更明智的选择,从而更好地应对挑战,实现项目目标。
490 0
|
C语言 C++
实现两个变量值的互换[C语言和C++的区别]
实现两个变量值的互换[C语言和C++的区别]
212 0
|
11月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
9月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
368 12
|
7月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
197 0
|
7月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
313 0
|
10月前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
196 16