目录
建立二叉树
二叉树
一个节点有两个后驱节点,基本上二叉树的问题可以通过递归解决
二叉搜索树
左节点<父节点<右节点,二分查找快速定位数据,但当每次插入的元素为极值,二分搜索树退化成链表,查询从O(logn)降到O(n),搜索树的中序遍历是有序序列
自平衡二叉搜索树
自己调整树的高度,使得左右子树高度差不超过1
红黑树
非严格平衡的二叉搜索树,查找O(logn),根节点和叶子节点为黑,红节点的子节点为黑,简单路径上的黑节点数目相同
B树
自平衡的多叉搜索树,节点存储数据
B+树
自平衡的多叉搜索树,只有叶子节点存储数据,叶子节点之间构造双向链表
树节点
struct Node {
int value;
Node *left;
Node *right;
};
这里使用层次建立二叉树,使用队列,将要建立的层入队,建完节点就入队
Node *buildTree(std::vector<int> nums) {
if (nums.empty())
return nullptr;
Node *root = new Node{nums[0], nullptr, nullptr};
std::queue<Node *> level;
level.emplace(root);
int i = 1;
while (i < nums.size()) {
Node *node = level.front();
level.pop();
// left
node->left = new Node{nums[i], nullptr, nullptr};
level.emplace(node->left);
++i;
if (i == nums.size())
break;
// right
node->right = new Node{nums[i], nullptr, nullptr};
level.emplace(node->right);
++i;
}
return root;
}
二叉树的前序遍历
递归遍历通用套路,只在访问元素和继续递归遍历子节点的顺序不同
/**
* 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:
vector<int> ans;
void dfs(TreeNode* root) {
if (root == nullptr) {
return;
}
ans.emplace_back(root->val);
dfs(root->left);
dfs(root->right);
}
vector<int> preorderTraversal(TreeNode* root) {
dfs(root);
return ans;
}
};
二叉树的中序遍历
/**
* 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:
vector<int> ans;
void dfs(TreeNode* root) {
if (root == nullptr) {
return;
}
dfs(root->left);
ans.emplace_back(root->val);
dfs(root->right);
}
vector<int> inorderTraversal(TreeNode* root) {
dfs(root);
return ans;
}
};
二叉树的后序遍历
/**
* 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:
vector<int> ans;
void dfs(TreeNode* root) {
if (root == nullptr) {
return;
}
dfs(root->left);
dfs(root->right);
ans.emplace_back(root->val);
}
vector<int> postorderTraversal(TreeNode* root) {
dfs(root);
return ans;
}
};
二叉树的层序遍历
前序、中序、后序都是深度优先搜索,层序遍历是广度优先搜索,深度优先搜索一般通过栈或者递归实现,广度优先搜索一般通过队列实现
将起点放入队列,每一层遍历先记录队列中的元素个数,这是这一层需要遍历的个数,访问当前节点,出队,然后将子节点入队,直到队空
/**
* 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:
vector<vector<int>> levelOrder(TreeNode* root) {
if (root == nullptr)
return {};
vector<vector<int>> ans;
queue<TreeNode*> level;
level.push(root);
while (!level.empty()) {
int levelSize = level.size();
vector<int> temp;
while (levelSize--) {
TreeNode* node = level.front();
level.pop();
temp.push_back(node->val);
if (node->left)
level.push(node->left);
if (node->right)
level.push(node->right);
}
ans.emplace_back(std::move(temp));
}
return ans;
}
};
二叉树的最大深度
树的深度,往下遍历一次子节点深度就加一,取左右子节点较大的加一返回就行了
/**
* 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:
int maxDepth(TreeNode* root) {
if (root == nullptr) {
return 0;
}
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
};
翻转二叉树
把指向左右节点的指针交换就行了
/**
* 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* invertTree(TreeNode* root) {
if (root == nullptr) {
return nullptr;
}
swap(root->left, root->right);
invertTree(root->left);
invertTree(root->right);
return root;
}
};
对称二叉树
递归解决,如果都空那么相等,否则有一个为空那么不相等,剩下就是都不为空,判断元素是否相等,接着递归判断左边的左子树是否等于右边的右子树,左边的右子树是否等于右边的左子树
/**
* 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:
bool isSymmetric(TreeNode* root) { return dfs(root, root); }
bool dfs(TreeNode* left, TreeNode* right) {
if (left == nullptr && right == nullptr) {
return true;
}
if (left == nullptr || right == nullptr) {
return false;
}
return left->val == right->val && dfs(left->left, right->right) &&
dfs(left->right, right->left);
}
};
二叉树的直径
其实就是找两个子节点深度的最大和,去计算每个节点的深度,然后在过程中记录最大的和
/**
* 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:
int ans = 0;
int dfs(TreeNode* root) {
if (root == nullptr)
return 0;
int left = dfs(root->left);
int right = dfs(root->right);
ans = max(ans, left + right);
return 1 + max(left, right);
}
int diameterOfBinaryTree(TreeNode* root) {
dfs(root);
return ans;
}
};
将有序数组转换为二叉搜索树
108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode)
通过取中间元素作为根节点,左半部分递归地构造左子树,右半部分递归地构造右子树
/**
* 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 *sortedArrayToBST(vector<int> &nums) {
return build(nums, 0, nums.size() - 1);
}
TreeNode *build(vector<int> &nums, int left, int right) {
if (left > right)
return nullptr;
int root = (left + right) / 2;
return new TreeNode(nums[root], build(nums, left, root - 1), build(nums, root + 1, right));
}
};
验证二叉搜索树
即左边的小于根小于右边的,不仅仅是这样,根必须得比左子树的都要大,比右子树的都要小,因此对于每个节点都需要小于某个值大于某个值
/**
* 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:
bool assist(TreeNode* root, long long low, long long high) {
if (root == nullptr)
return true;
if (root->val <= low || root->val >= high)
return false;
return assist(root->left, low, root->val) &&
assist(root->right, root->val, high);
}
bool isValidBST(TreeNode* root) {
return assist(root, LONG_LONG_MIN, LONG_LONG_MAX);
}
};
二叉搜索树中第K小的元素
230. 二叉搜索树中第 K 小的元素 - 力扣(LeetCode)
二叉搜索树中序遍历的结果就是有序序列
/**
* 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:
vector<int> nums;
void inOrder(TreeNode* root) {
if (root == nullptr)
return;
inOrder(root->left);
nums.push_back(root->val);
inOrder(root->right);
}
int kthSmallest(TreeNode* root, int k) {
inOrder(root);
return nums[k - 1];
}
};
二叉树的右视图
即每一个深度都装一个最右边的节点,深度优先遍历右节点,根据已经装的节点数和深度判断这个深度有没有装节点
/**
* 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:
vector<int> ans;
void dfs(TreeNode* root, int depth) {
if (root == nullptr)
return;
if (depth == ans.size())
ans.push_back(root->val);
dfs(root->right, depth + 1);
dfs(root->left, depth + 1);
}
vector<int> rightSideView(TreeNode* root) {
dfs(root, 0);
return ans;
}
};
二叉树展开为链表
方法一:
要求链表顺序和二叉树的前序遍历顺序一样,那么可以先前序遍历放到容器里,然后根据遍历顺序把节点链接起来
/**
* 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:
vector<TreeNode*> assist;
void dfs(TreeNode* root) {
if (root == nullptr)
return;
assist.push_back(root);
dfs(root->left);
dfs(root->right);
}
void flatten(TreeNode* root) {
dfs(root);
for (int i = 0; i < int(assist.size()) - 1; i++) {
assist[i]->left = nullptr;
assist[i]->right = assist[i + 1];
}
}
};
方法二:
还有一种空间复杂度为O(h)的,h为二叉树的高度,这是因为它看起来是O(1)的空间,实际上因为函数调用栈是O(h)的空间,反前序遍历,从前序遍历最后一个节点开始操作,记录前驱节点的指针
/**
* 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* prev = nullptr;
void flatten(TreeNode* root) {
if (root == nullptr) {
return;
}
flatten(root->right);
flatten(root->left);
root->left = nullptr;
root->right = prev;
prev = root;
}
};
从前序与中序遍历序列构造二叉树
105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)
前序遍历是根在前面,然后是左子树,再是右子树,中序遍历是左子树-根-右子树,通过前序遍历可以找到根,在中序遍历里面确定根的位置后可以知道左子树的长度和右子树的长度,如此可以在前序遍历中找到左子树和右子树的根,如此递归下去
为了更快的找到中序遍历里面根的位置,可以用哈希表存储下来根对应的索引
/**
* 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:
unordered_map<int, int> index;
vector<int> preorder, inorder;
TreeNode* build(int pre_left, int pre_right, int in_left, int in_right) {
if (pre_left > pre_right)
return nullptr;
TreeNode* root = new TreeNode(preorder[pre_left]);
int in_root = index[preorder[pre_left]];
int left_size = in_root - in_left;
root->left =
build(pre_left + 1, pre_left + left_size, in_left, in_root - 1);
root->right =
build(pre_left + left_size + 1, pre_right, in_root + 1, in_root);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
this->preorder = preorder;
this->inorder = inorder;
for (int i = 0; i < inorder.size(); i++)
index[inorder[i]] = i;
return build(0, preorder.size() - 1, 0, inorder.size() - 1);
}
};
路径总和
要判断从根节点到叶子节点的路径和是否等于目标,可以每层减去节点值更新目标值,判断叶子节点值是否等于目标值
/**
* 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:
bool hasPathSum(TreeNode* root, int targetSum) {
if (root == nullptr) {
return false;
}
if (root->left == nullptr && root->right == nullptr) {
return targetSum == root->val;
}
return hasPathSum(root->left, targetSum - root->val) ||
hasPathSum(root->right, targetSum - root->val);
}
};
路径总和 II
要判断从根节点到叶子节点的路径和是否等于目标,可以每层减去节点值更新目标值,判断叶子节点值是否等于目标值,在这过程中记录下路径,注意回溯时pop即可
/**
* 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:
int target, ans = 0;
unordered_map<long long, int> prefix;
void dfs(TreeNode* root, long long sum) {
if (root == nullptr)
return;
sum += root->val;
if (prefix.find(sum - target) != prefix.end())
ans += prefix[sum - target];
++prefix[sum];
dfs(root->left, sum);
dfs(root->right, sum);
--prefix[sum];
}
int pathSum(TreeNode* root, int targetSum) {
target = targetSum;
prefix[0] = 1;
dfs(root, 0);
return ans;
}
};
路径总和 III
先用深度遍历记录每个节点到根的路径和,然后用哈希表记录前缀和出现的次数,两个前缀和之差就是节点到节点的路径和,要注意回溯时次数减一
/**
* 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:
int target, ans = 0;
unordered_map<long long, int> prefix;
void dfs(TreeNode* root, long long sum) {
if (root == nullptr)
return;
sum += root->val;
if (prefix.find(sum - target) != prefix.end())
ans += prefix[sum - target];
++prefix[sum];
dfs(root->left, sum);
dfs(root->right, sum);
--prefix[sum];
}
int pathSum(TreeNode* root, int targetSum) {
target = targetSum;
prefix[0] = 1;
dfs(root, 0);
return ans;
}
};
二叉树的最近公共祖先
236. 二叉树的最近公共祖先 - 力扣(LeetCode)
递归查找两个节点的所在地,如果两个节点一个在root的左子树一个在右子树,说明root就是公共祖先,并且因为是递归,root就是最近的,如果不是,往左右子树递归的时候返回来空的,那说明最近公共祖先在非空的一侧,如果root就是两个节点之一,那么就直接返回
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == nullptr || root == p || root == q)
return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left == nullptr)
return right;
if (right == nullptr)
return left;
return root;
}
};
二叉树中的最大路径和
124. 二叉树中的最大路径和 - 力扣(LeetCode)
深度遍历计算每个节点的最大路径,然后记录左右两边和的最大值
/**
* 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 {
int ans = INT_MIN;
int dfs(TreeNode* root) {
if (root == nullptr)
return 0;
int left = max(0, dfs(root->left));
int right = max(0, dfs(root->right));
ans = max(ans, left + right + root->val);
return root->val + max(left, right);
}
public:
int maxPathSum(TreeNode* root) {
dfs(root);
return ans;
}
};
Main
#include<iostream>
#include<queue>
#include<vector>
struct Node {
int value;
Node *left;
Node *right;
};
Node *buildTree(std::vector<int> nums) {
if (nums.empty())
return nullptr;
Node *root = new Node{nums[0], nullptr, nullptr};
std::queue<Node *> level;
level.emplace(root);
int i = 1;
while (i < nums.size()) {
Node *node = level.front();
level.pop();
// left
node->left = new Node{nums[i], nullptr, nullptr};
level.emplace(node->left);
++i;
if (i == nums.size())
break;
// right
node->right = new Node{nums[i], nullptr, nullptr};
level.emplace(node->right);
++i;
}
return root;
}
void levelShow(Node *root) {
if (root == nullptr)
return;
std::queue<Node *> level;
level.emplace(root);
while (level.empty() == false) {
root = level.front();
level.pop();
std::cout << root->value;
if (root->left)
level.emplace(root->left);
if (root->right)
level.emplace(root->right);
}
}
int main() {
std::vector<int> nums(10);
for (int i = 0; i < 10; ++i)
nums[i] = i;
Node *tree = buildTree(nums);
levelShow(tree);
}