声明:该文章仅为我个人的学习记录,由于我只是个新手,因此有错误之处还请各位见谅!也恳请大家能积极提出建议!
二叉树
二叉树就是树形结构里比较特殊的一种,即每个结点最多只有左右两个子树,整棵树中不存在度>2的结点
二叉树的左右子树是有顺序的,即使只有一个子树,也得区分它到底是左子树还是右子树
二叉树的五种基本形态:
空树
只有根节点
只有根节点+左子树
只有根节点+右子树
根节点+左、右两个子树
满二叉树/完全二叉树/斜二叉树
满二叉树:顾名思义,一切都是满满当当的,所有叶子都在最后一层,而且每个子结点的度都为2,即每个子结点都拥有左右子树
同样的深度下,满二叉树的叶子和结点最多
完全二叉树的范围稍大,而且包含了满二叉树在内
注:满二叉树一定是完全二叉树,而完全二叉树不一定是满二叉树
完全二叉树的特点是,按从上到下,从左到右的顺序给结点编号的话,是不会出现空挡的,如果出现了,就不是完全二叉树了,
换种讲法就是,把最后一层剔除掉的话,完全二叉树就是满二叉树了,且最后一层的叶子集中在左边,秉持有左才能有右的概念,即如果存在度为1的结点,那这个结点的子树一定是左子树,不可能是右子树
同样的结点数下,完全二叉树的深度最低
斜二叉树:只有左子节点或只有右子节点的二叉树称为斜二叉树
斜二叉树的结点数 = 深度
性质
都是一些简单的关系式
(设叶子结点的个数为n0,度为1的结点数为n1,度为2的结点数为n2)
1:在二叉树的第x层中,结点最多为
2^(x-1)
2:在深度为x的二叉树里,结点的总和不会超过
(2^x)-1
3:二叉树的结点总数
n = n0 + n1 + n2
4:二叉树的总连接线
x = n - 1 = n1 + 2 * n2
(n - 1是因为每个结点都会有一条线指向双亲,只有根节点没有,所以x = n - 1)
(n1 + 2 * n2是因为每个结点的线也可以看作是结点指向他们的孩子的,这样根节点也算进去了,而叶子没有孩子,自然也没有指向孩子的线,度为1就有一根线,度为2就有两根线,所以x = n1 + 2 * n2)
5:由性质4可以得出n0 + n1 + n2 - 1 = n1 + 2 * n2
最后得到
n0 = n2 +1
即叶子数 = 度为2的结点数 + 1;
6:由性质2可得有n个结点的的完全二叉树的深度为
(log2 n)向下取整 + 1
7:在一颗结点数为n完全二叉树中,设i是各个结点的编号,且有1<= i <= n
当i = 1时,i所在的结点为根节点
当2i > n时,该结点要么是叶子,要么左孩子的编号是2i
当2i + 1 > n 时,该结点要么无右孩子,要么右孩子的编号是2i+1
二叉树的存储结构
顺序结构
一般用于完全二叉树,用于其他情况会造成空间的浪费
链式结构
二叉链表:每个结点除了数据域,还有两个指针域,用来指向左右孩子
也可以在增加一个指向双亲的指针域,可以叫三叉链表
二叉树的遍历
利用递归的形式,可以非常简洁的遍历二叉树,甚至只要改变一下访问语句于递归语句的顺序,就可以做到以前序,中序,后序的方式遍历整个二叉树,因此这里就不写了,写个迭代法吧
迭代遍历
说一下思路,递归遍历很简单是因为二叉树遍历本身就和递归的性质很像,对每一个结点的操作都是一样的,假如是前序遍历(中,左,右),先输出当前结点,然后跳转到左子节点,然后再次输出当前结点,再判断是否有左子结点…说着说着就变成递归了
我们用一个栈来模拟递归的过程,给出一个二叉树,我们先判断这树的根节点是不是空的,不是空的,就把根节点压入栈,然后用一个临时指针指向根节点,并且输出根节点数值的同时从栈中弹出这个结点,然后再利用临时指针判断这个结点是否有右子结点,有就入栈,再判断是否有左子结点,有就入栈,因为栈的特性,所以现在左子结点在栈顶,然后我们就可能开始循环了,再次输出栈顶结点的值,然后再将右,左子结点入栈,所以每次输出的时候都是输出左子结点,而右子结点则被挡在后面,直到没有左子结点为止,这就符合前序遍历的规则
前序:
vector<int> qianxu (treenode* head)
{
vector<int> res;
stack<treenode*>st;
if(head) st.push(head);
while(!st.empty())
{
treenode* p = st.top();
st.pop();
res.push_back(p->val);
//前序是要先左后右,但栈是先进后出,所以反过来入栈
if(p->right)st.push(p->right);
if((p->left))st.push(p->left);
}
return res;
}
中序:
vector<int> zhongxu(treenode* head)
{
vector<int> res;
stack<treenode*> st;
treenode* p = head;
while(p != nullptr || !st.empty())
{
if( p!= nullptr)
{
//先是一路往左,把左结点全部入栈,然后再次往左,p == nullptr
st.push(p);
p = p->left;
}
else
{
p = st.top();
st.pop();
res.push_back(p->val);
p = p->right;
}
}
return res;
}
具体语句待补充