二叉树基础知识总结:
参考:二叉树知识点最详细最全讲解-CSDN博客二叉树的基本操作(C++实现)_二叉树c++实现-CSDN博客labuladong算法小抄笔记2:数据结构---二叉树1 - 知乎 (zhihu.com)
1. 定义二叉树
/*定义二叉树的结构*/
typedef struct Node
{
int data; //数据域
struct Node *lchild, *rchild; //左子树和右子树
} * BiTree, BiNode;
//整棵树和结点名称
力扣上的定义:
// 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) {}
};
做题的时候可能就以力扣的这个定义为准了。
2. 创建二叉树
二叉树有两种存储方式:链式存储和顺序存储
链式存储很好理解,就是用链表的方式,一个节点存储了值,以及指向左右子树的指针;
顺序存储就是用数组,将二叉树节点按顺序存储下来。
这两种方式的创建方式也有所不同
2.1 链式存储
递归法
TreeNode* createTree() {
int val;
cin >> val;
if (val == -1) { // 输入-1表示该节点为空
return NULL;
}
TreeNode* root = new TreeNode(val);
root->left = createTree(); // 递归创建左子树
root->right = createTree(); // 递归创建右子树
return root;
}
将顺序存储转化为链式存储
TreeNode* buildTree(vector<int>& nums)
{
if (nums.empty()) return nullptr;
TreeNode *root = new TreeNode(nums.front());
vector<TreeNode*> q = {root};
int i = 1;
while(!q.empty() && i < nums.size())
{
TreeNode *cur = q.front();
q.assign(q.begin() + 1, q.end());
if(i < nums.size())
{
cur->left = new TreeNode(nums[i++]);
q.push_back(cur->left);
}
if(i < nums.size())
{
cur->right = new TreeNode(nums[i++]);
q.push_back(cur->right);
}
}
return root;
}
3. 遍历二叉树
3.1 前序遍历
//根左右
void preOrderTraversal(TreeNode* root) {
if (root == nullptr) {
return;
}
cout << root->val << " ";
preOrderTraversal(root->left);
preOrderTraversal(root->right);
}
3.2 中序遍历
//左根右
void inOrderTraversal(TreeNode* root) {
if (root == nullptr) {
return;
}
inOrderTraversal(root->left);
cout << root->val << " ";
inOrderTraversal(root->right);
}
3.3 后序遍历
//左右根
void postOrderTraversal(TreeNode* root) {
if (root == nullptr) {
return;
}
postOrderTraversal(root->left);
postOrderTraversal(root->right);
cout << root->val << " ";
}
遍历的过程也可以将值存进去。
以力扣94题二叉树的中序遍历为例:给定一个二叉树的根节点 root
,返回 它的 中序 遍历 。
class Solution {
public:
void InorderTraversal(TreeNode* root, vector<int>& nums){
if(root == nullptr) return;
InorderTraversal(root->left, nums);
nums.push_back(root->val);
InorderTraversal(root->right, nums);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ans;
InorderTraversal(root, ans);
return ans;
}
};
二叉树的大家族汇总:
//二叉树
//结点类型
template <typename T>
struct BinTreeNode
{
T data; //结点中存储的数据
BinTreeNode<T> *leftChild, *rightChild; //左子树和右子树
BinTreeNode() : leftChild(NULL), rightChild(NULL) {} //无参构造函数
BinTreeNode(T x, BinTreeNode<T> *l = NULL, BinTreeNode<T> *r = NULL) : data(x), leftChild(l), rightChild(r) {} //带默认值的有参构造参数
};
//二叉树类
template <typename T>
class BinaryTree
{
public:
//==========二叉树构造与析构==========//
//构造函数
BinaryTree() : root(NULL) {}
//指定结束标志的构造函数
BinaryTree(T value) : RefValue(value), root(NULL) {}
//析构函数
~BinaryTree() { Destroy(root); }
//==========二叉树的创建==========//
//使用广义表创建二叉树,以'#'字符代表结束
void CreateBinTree() { CreateBinTree(root); }
//前序遍历创建二叉树(前序遍历的应用),用#表示空结点
void CreateBinTree_PreOrder() { CreateBinTree_PreOrder(root); }
//使用先序遍历和中序遍历创建二叉树
void CreateBinTreeBy_Pre_In(const char *pre, const char *in)
{
int n = strlen(pre);
CreateBinTreeBy_Pre_In(root, pre, in, n);
}
//使用后序遍历和中序遍历创建二叉树
void CreateBinTreeBy_Post_In(const char *post, const char *in)
{
int n = strlen(post);
CreateBinTreeBy_Post_In(root, post, in, n);
}
//==========二叉树的遍历==========//
//先序遍历(递归)
void PreOrder() { PreOrder(root); }
//中序遍历(递归)
void InOrder() { InOrder(root); }
//后序遍历(递归)
void PostOrder() { PostOrder(root); }
//先序遍历(非递归)
void PreOrder_NoRecurve() { PreOrder_NoRecurve(root); }
//中序遍历(非递归)
void InOrder_NoRecurve() { InOrder_NoRecurve(root); }
//后序遍历(非递归)
void PostOrder_NoRecurve() { PostOrder_NoRecurve(root); }
//层次遍历(非递归)
void LevelOrder() { LevelOrder(root); }
//==========获取结点==========//
//获取二叉树的根节点
BinTreeNode<T> *getRoot() const
{
return root;
}
//获取current结点的父节点
BinTreeNode<T> *Parent(BinTreeNode<T> *current)
{
return (root == NULL || root == current) ? NULL : Parent(root, current); //如果没有根节点或current结点就是root结点,就没有父节点
}
//获取current结点的左结点
BinTreeNode<T> *LeftChild(BinTreeNode<T> *current)
{
return (current != NULL) ? current->leftChild : NULL;
}
//获取current结点的右结点
BinTreeNode<T> *RightChild(BinTreeNode<T> *current)
{
return (current != NULL) ? current->rightChild : NULL;
}
//==========成员函数==========//
//销毁函数
void Destroy() { Destroy(root); }
//拷贝二叉树(前序遍历的应用)
BinaryTree(BinaryTree<T> &s)
{
root = Copy(s.getRoot());
}
//判断两颗二叉树是否相等(前序遍历的应用)
bool operator==(BinaryTree<T> &s)
{
return (equal(this->getRoot(), s.getRoot()));
}
//计算二叉树的结点的个数(后序遍历的应用)
int Size() { return Size(root); }
//计算二叉树的高度(后序遍历的应用)
int Height() { return Height(root); }
//判断二叉树是否为空
bool Empty() { return (root == NULL) ? true : false; }
//以广义表的形式输出二叉树(前序遍历的应用)
void PrintBinTree() { PrintBinTree(root); }
private:
BinTreeNode<T> *root; //根节点
T RefValue; //数据输入停止的标志,需要一个构造函数
};
4. 搜索算法
主要用于树和图论问题
参考bfs与dfs详解(例题+模板c++代码) - 知乎 (zhihu.com)深度优先搜索(DFS)和广度优先搜索(BFS)-CSDN博客
4.1 深度优先搜索(DFS)
(前序遍历、中序遍历、后序遍历)
一般用栈数据结构或递归
4.2 广度优先搜索(BFS)
一般都用队列存储结构
* 栈和队列还有图论还没学到,先留个坑
-------已经填坑啦:记录刷题中遇到的知识点方便复习(十五)(二叉树2)-CSDN博客
三十六、LeetCode第三十六题(104)
二叉树的最大深度
用递归,根节点的深度总是左右子树最大深度加一。
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root == nullptr) return 0;
int l = maxDepth(root->left);
int r = maxDepth(root->right);
int ans = max(l, r)+1;
return ans;
}
};
三十七、LeetCode第三十七题(226)
翻转二叉树
用递归,左右都翻转,注意用临时节点保存。
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root == nullptr) return nullptr;
TreeNode *right = invertTree(root->left);
TreeNode *left = invertTree(root->right);
root->left = left;
root->right = right;
return root;
}
};
三十七、LeetCode第三十七题(101)
对称二叉树
给你一个二叉树的根节点 root
, 检查它是否轴对称。
还是用递归,比较左右子树值是否相等,然后递归左右子树的右左子树值是否相等
class Solution {
public:
bool check(TreeNode *p, TreeNode *q){
if(!p && !q) return true;
if(!p || !q) return false;
if(p->val == q->val && check(p->left, q->right) && check(p->right, q->left)) return true;
return false;
}
bool isSymmetric(TreeNode* root) {
return check(root->left, root->right);
}
};
这题还有很多思路,比如将二叉树翻转,比较前后二叉树是否相等
我一开始想的是用中序遍历,存入数组,然后判断数组是否对称,但是这样会有很多特殊情况不行。
三十七、LeetCode第三十七题(543)
二叉树的直径
给你一棵二叉树的根节点,返回该树的 直径 。
二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root
。
两节点之间路径的 长度 由它们之间边数表示。
我感觉这道题还是有难度的,可能是我太菜了。。。
递归法,找到左子树的最大深度和右子树的最大深度,两个最大深度相加就是该节点的直径,递归的过程不断更新最长直径。
class Solution {
public:
int depth(TreeNode *root, int& ans){
if(root == nullptr) return 0;
int l = depth(root->left, ans);
int r = depth(root->right, ans);
ans = max(ans, l+r); //更新最长直径
return max(l, r)+1;
}
int diameterOfBinaryTree(TreeNode* root) {
int ans = 0;
depth(root, ans);
return ans;
}
};
如果不想传参可以用全局变量。