二叉树
二叉树是每个节点最多有两个子树的树结构,不存在度大于2的结点。
它有以下五种形态
二叉树的性质
-
二叉树的第i层至多有2i个结点;
-
深度为k的二叉树至多有2k-1个结点;
-
对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。
-
包含n个节点的二叉树的高度至少为log2 (n+1)。
斜树
所有结点都只有左子树或右子树的二叉树被称为斜树。
满二叉树
满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点。它只有度为0和度为2的节点,没有度为1的节点,并且所有的度为0的节点都在同一层上。
假设一棵满二叉树的高度为h,则它的总节点数为奇数,为2i-1,叶子节点数为2h,第k层的结点数是:2k-1。
完全二叉树
一颗二叉树中,只有最下面两层节点的度可以小于2,并且最下层的叶节点集中在靠左的若干位置上。
若设二叉树的深度为h,除第 h 层外,其它各层 (1~(h-1)层) 的结点数都达到最大个数,第h层所有的结点都连续集中在最左边。当结点的度为 1 时,该结点只能拥有左子树。
二叉树的节点定义
每一个节点在保存自身数据的同时,拥有指向左右子树的指针
class TreeNode
{
public:
BiTreeNode(int v=0, TreeNode* l = nullptr, TreeNode* r = nullptr):val(v),left(l),right(r)
{
}
int val;
TreeNode* left;
TreeNode* right;
};
二叉树的定义
每棵树都会有一个根节点root
class BinaryTree{
public:
BinaryTree(TreeNode* r=nullptr):root(r)
{
}
~BinaryTree()
{
clearNode(root);
root=nullptr;
}
private:
void BinaryTree::clearNode(TreeNode* p)
{
if (p != nullptr)
{
clearNode(p->left);
clearNode(p->right);
delete p;
}
}
TreeNode* root;
}
树的前序遍历
前序遍历的顺序是:根左右
上述图片的前序遍历:12457368
从根节点1出发,按照根左右的顺序,访问左节点2,得到1->2,再将左节点2当作根节点,访问节点2的左节点,得到1->2->4,将4当作根节点,左右节点为空,则节点2的左子树访问完成,接着访问节点2的右子树,得到1->2->4->5
//递归
void preorder(TreeNode* root, vector<int>& res) {
if (root == nullptr) return;
res.push_back(root->val);
preorder(root->left);
preorder(root->right);
}
//非递归
vector<int> preorder(TreeNode* root) {
if (root == nullptr) return {};
vector<int> res;
stack<TreeNode*> st;
st.push(root);
while (!st.empty())
{
root = st.top();
res.push_back(root->val);
st.pop();
if (root->right != nullptr) st.push(root->right);
if (root->left != nullptr) st.push(root->left);
}
return res;
}
二叉树的中序遍历
仍采用前序遍历的图
中序遍历的顺序:左根右
从最下面一层开始,将左节点合并到父节点的左边,右节点合并到父节点的右边,然后整体作为一个节点,不断重复这个操作,直到根节点
中序遍历的结果:42751683
//递归
void inorder(TreeNode* root, vector<int>& res) {
if (root == nullptr) return;
inorder(root->left);
res.push_back(root->val);
inorder(root->right);
}
//非递归
vector<int> inorder(TreeNode* root) {
if (root == nullptr) return {};
stack<TreeNode*> st;
vector<int> res;
while (root || !st.empty())
{
while (root != nullptr)
{
st.push(root);
root = root->left;
}
root = st.top();
st.pop();
res.push_back(root->val);
root = root->right;
}
return res;
}
二叉树的后序遍历
后序遍历的顺序是:左右根
从节点1开始,按照左右根的顺序,找到节点1的左节点(节点2),将节点2当作根节点,找到节点2的左节点(节点4),将节点4作为根节点,找寻左节点,左节点为空,找寻节点4的右节点,右节点为空,则轮到了节点4,以节点2作为根节点的左子树遍历完毕,则遍历节点2的右节点,以节点5作为根节点继续遍历。
结果:47528631
//递归
void postorder(TreeNode* root, vector<int>& res) {
if (root == nullptr) return;
postorder(root->left);
postorder(root->right);
res.push_back(root->val);
}
//非递归
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> res;
if (root == nullptr) return res;
st.push(root);
while (!st.empty())
{
root = st.top();
st.pop();
res.push_back(root->val);
if (root->left) st.push(root->left);
if (root->right) st.push(root->right);
}
reverse(res.begin(), res.end());
return res;
}
层序遍历
判断根节点是否为空,不为空则可以将根节点放进队列里,根节点此时存储着第1层的所有节点,通过遍历的方式获取此时的queue的所有结点的左右结点就可以得到第2层的所有结点,重复此操作,直到队列为空
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if (root == nullptr)
{
return res;
}
queue<TreeNode*> q;
q.push(root);
while (!q.empty())
{
int sz = q.size();
vector<int> tmp;
for (int i = 0; i < sz; i++)
{
root = q.front();
q.pop();
tmp.push_back(root->val);
if (root->left) q.push(root->left);
if (root->right) q.push(root->right);
}
res.push_back(tmp);
}
return res;
}
通过前序和中序数组建立二叉树
前序数组按照根左右的原则遍历,排在左边的为根节点,中序遍历的根节点在中间,我们找到根节点后,查找根节点在中序数组的位置,左边则为树的左子树的节点,右边的则为树的右子树的节点,通过递归的方式不断建立当前节点的左右子树,直到达到终止节点
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
//用哈希数组存储中序数组的元素对应的的下标
unordered_map<int, int> mp;
for (int i = 0; i < inorder.size(); i++)
{
mp[inorder[i]] = i;
}
return createTree(preorder, mp,0, preorder.size() - 1, 0, preorder.size() - 1);
}
//pl,pr为此次操作建立子树的元素对应的先序数组的下标,il,ir则为中序数组的
TreeNode* createTree(vector<int>& preorder, unordered_map<int, int>& mp,
int pl, int pr, int il, int ir)
{
if (pl > pr) return nullptr;
TreeNode* root = new TreeNode(preorder[pl]);
//从哈希数组获取根节点对应的下标
int index = mp[preorder[pl]];
//左子树的节点数量index-il,所以左子树在先序数组的范围是[pl+1,pl+index-il]
root->left = createTree(preorder, mp, pl + 1, pl + index - il, il, index - 1);
root->right = createTree(preorder, mp, pl + index - il + 1, pr, index + 1, ir);
return root;
}
通过中序和后序数组建立二叉树
思路与前面的一样,只是根节点在最右边
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
unordered_map<int, int> mp;
for (int i = 0; i < postorder.size(); i++)
{
mp[inorder[i]] = i;
}
TreeNode* root = createTree(postorder, mp, 0, inorder.size() - 1, 0, postorder.size() - 1);
return root;
}
TreeNode* createTree(vector<int>& postorder, unordered_map<int, int>& mp, int sl, int sr, int il, int ir)
{
if (sl > sr) return nullptr;
TreeNode* root = new TreeNode(postorder[sr]);
int index = mp[postorder[sr]];
root->left = createTree(postorder, mp, sl, sl + index - il - 1, il, index - 1);
root->right = createTree(postorder, mp, sl + index - il, sr - 1, index + 1, ir);
return root;
}