二叉树——概念与操作
二叉树——概念与操作
基本概念
二叉树的基本概念与性质
- 基本概念
二叉树有5种基本形态,特殊的二叉树有满二叉树、完全二叉树、二叉排序树和平衡二叉树。
-
性质
-
存储结构
二叉树的存储结构有顺序存储结构和链式存储结构两种。满二叉树和完全二叉树采取顺序存储结构比较合适,树中的节点号可以唯一的反映出节点之间的逻辑关系,可以节省空间也可以利用数组元素的下标确定节点在二叉树中的位置以及节点之间的关系。一般二叉树采用链式结构存储,二叉链表中包含3个域,分别是数据域data、左指针lchild和右指针rchild。二叉树的链式存储结构描述如下:
// 二叉树链式存储结构
typedef struct BiNode {
ElemType data; // 数据域
struct BiNode *lchild, *rchild; // 左右孩子指针
}BiNode, *BiTree;
线索二叉树的基本概念与性质
- 基本概念
线索二叉树的实质就是对一个非线性结构进行线性化操作,使在这个访问序列中的每一个节点都有一个直接前驱和一个直接后继,引入线索二叉树是为了加快查找节点前驱和后继的速度。
在有N个节点的二叉树中,一共有N+1空指针。在二叉树线索化的时候,通常规定:若无左子树,另lchild指向其前驱节点;若无右子树,则rchild指向其后继节点。还需要两个标志域ltag和rtag表明当前指针域所指的对象是左子节点还是直接前驱。保标志域的含义如下所示:
ltag: 0 lchild域指示节点的左孩子;1 lchild域指示节点的前驱;
rtag: 0 rchild域指示节点的右孩子;1 rchild域指示节点的后继。
线索二叉树的存储结构描述如下所示:
typedef struct ThreadNode {
ElemType data; // 数据域
struct ThreadNode *lchild, *rchild; // 左右孩子指针
int ltag, rtag; // 左右线索标志
} ThreadNode, *TreadTree;
二叉树的操作
二叉树的遍历
- 先序遍历
由于这种算法每一个节点都访问且仅访问一次,所以算法的时间复杂度为O(n),在最坏的情况下,算法的空间复杂度为O(n).
// 递归实现先序遍历二叉树
void PreOrder(BiTree T) {
if (T != NULL) {
visit(T);
PreOrder(T -> lchild);
PreOrder(T -> rchild);
}
}
/*非递归实现先序遍历二叉树
算法思路:
需要借助栈来实现,从根节点开始遍历二叉树,如果节点不为空
先输出节点的数据,节点入栈,然后指针指向节点的左孩子,如
果节点为空那么从占中弹出这个节点,并且将指针指向节点的右
孩子,重复这个过程直到栈为空,并且指针指向也为空为止。
*/
void PreOrder2(BiTree T) {
InitStack(S);
BiTree p = T;
while(p || !isEmpty(S)) {
if (p) {
visit(p);
push(S, p);
p = p -> lchild;
} else {
pop(S, p);
p = p -> rchild;
}
}
}
// 算法的时间复杂度和空间复杂度都是O(n)
- 中序遍历
由于这种算法每一个节点都访问且仅访问一次,所以算法的时间复杂度为O(n),在最坏的情况下,算法的空间复杂度为O(n).
// 递归算法实现中序遍历二叉树
void InOrder(BiTree T) {
if (T != NULL) {
InOrder(T -> lchild);
visit(T);
InOrder(T -> rchild);
}
}
/*非递归实现中序遍历二叉树
算法思路:
需要借助栈来实现,从根节点开始遍历二叉树,如果节点不为空
将节点入栈,然后指针指向节点的左孩子,如果节点为空,那么
从栈中弹出这个节点,输出节点的数据,并且将指针指向节点的
右孩子,重复这个过程直到栈为空,并且指针指向也为空为止。
*/
void InOrder2(BiTree T) {
InitStack(S);
BiTree p = T;
while(p || !isEmpty(S