二叉树
1. 性质:
(1)从上往下第i层,最多有2^(i-1)个结点。
(2)深度为k的二叉树,最少有k个结点,最多有2^k - 1个结点。
(3)假设叶结点有n0个,度为2的非叶结点有n2个,则n0 = n2+1。
(4)完全二叉树,深度为k,则
2
k
−
1
−
1
<
n
<
=
2
k
−
1
2^{k-1}-1<n<=2^k-1
2k−1−1<n<=2k−1。n个结点,则
k
=
l
o
g
2
n
+
1
k=log_2^{n+1}
k=log2n+1 取上限。
2. 递归遍历
(1)中序遍历(左根右,从左往右看的顺序)
a
+
b
∗
c
−
d
−
e
/
f
a+b*c-d-e/f
a+b∗c−d−e/f
void inorder(TreeNode* root)
{
if(root == NULL) return;
inorder(root->left);
cout<<root->val<<" ";
inorder(root->right);
}
(2)前序遍历(根左右)
−
+
a
∗
b
−
c
d
/
e
f
-+a*b-cd/ef
−+a∗b−cd/ef
void preorder(TreeNode* root)
{
if(root == NULL) return ;
cout<<root->val<<" ";
preorder(root->left);
preorder(root->right);
}
(3)后序遍历(左右根)
a
b
c
d
−
∗
+
e
f
/
−
abcd-*+ef/-
abcd−∗+ef/−
void postOrder(TreeNode* root)
{
if(root==NULL) return;
postOrder(root->left);
postOrder(root->right);
cout<<root->val<<" ";
}
(4)层次遍历
−
+
/
a
∗
e
f
b
−
c
d
-+/a*efb-cd
−+/a∗efb−cd
void levelOrder(TreeNode* root)
{
if(root == NULL) return;
queue<TreeNode*> q;
q.push(root);
while(!q.empty())
{
TreeNode* node = q.front(); q.pop();
cout<<node->val<<" ";
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
}
}
3. 非递归遍历
(1)前序遍历:直接输出当前结点(根),将当前结点的右节点入栈,令当前结点等于左结点或栈顶,然后不断循环,直到栈为空。
void preOrder(TreeNode* root)
{
stack<TreeNode* n> s;
while(root)
{
cout<<root->val <<" "; //根
if(root->right) s.push_back(root->right); //右结点压栈
if(root->left) root = root->left; //访问左结点
else {
if(!s.empty()) {
root = s.top();
s.pop();
}
else return;
}
}
}
(2)中序遍历:将当前结点和它的左结点全部入栈,然后访问最左结点,再访问该最左结点的右节点,将该右节点的全部左结点入栈,不断循环,直到栈为空并且没有左结点。
void inOrder(TreeNode* root)
{
stack<TreeNode*> s;
while(root || !s.empty())
{
while(root->left) //当前结点的左结点全部压栈
{
s.push(root->left);
root = root->left;
}
if(!s.empty())
{
root = s.top(); s.pop(); cout << root->val << " "; //访问最左的结点
root = root->right; //当前结点等于右结点,继续访问所有的左结点
}
}
}
(3)后序遍历:先访问左边,右边,再访问根结点,因此要区分两种情况:访问完左边和访问完右边。首先将当前节点的全部左结点入栈,然后访问最左结点,对该最左结点更改状态为R,然后访问该最左结点的右结点,将该右结点的全部左结点入栈。当访问到的最左结点的状态为R,说明其左边的结点都访问过了,此时可以弹出该结点并打印。
struct Node
{
TreeNode* root;
enum tag{L,R};
Node(TreeNode* r = NULL) {root = r; tag = L; }
};
void postOrder(TreeNode* root)
{
stack<Node*> s; Node* n;
while(root || !s.empty())
{
while(root) //当前全部左结点入栈
{
n->root = root; n->tag = L; s.push(n);
root = root->left;
}
int continue1 = 1;
while(continue1 && !s.empty())
{
n = s.top(); s.pop(); root = n.root; //访问最左边结点
switch(n.tag)
{
case L: //访问完左边,访问root
n.tag = R; s.push(n);
root = root->right;
continue1 = 0; //将该右节点的全部左结点压栈
break;
case R: //访问完右边,访问root
cout<<root->val<<" "; //右边的结点依次弹栈打印,直到栈空
break;
}
}
}
}
4. 利用二叉树遍历的函数
(1)计算二叉树结点的个数
int count(TreeNode* root)
{
if(root==NULL) return 0;
return 1 + count(root->left) + count(root->right);
}
(2)计算二叉树的深度
int depth(TreeNode* root)
{
if(root == NULL) return 0;
return 1 + max(depth(root->left), depth(root->right));
}
5.二叉树的建立
(1)前序建立
void preCreate(string s, int &i, TreeNode* &root)
{
if(s[i] == ''@') {
root = NULL;
return;
}
root = new TreeNode(s[i]);
create(s, ++i, root->left);
create(s, ++i, root->right);
}
(2)中序后序建立类似
6. 线索化二叉树
(1)应用:
查找结点q的父节点:先假设q是父节点的左孩子,则找到q的最右的结点,该节点的后继应该为父节点;若不是,则q是父结点的右孩子,则找到该节点最左边的结点,该结点的前继为父结点。