树 算法知识总结
一,基础
1, 二叉树
(1) 第i层上至多有2^(i-1)个结点
(2) 深度为k的二叉树至多有2^k – 1个结点
(3) 对于任意一颗二叉树,终端结点数为n0,度为2的节点数为n2,n0 = n2 + 1
(4) 深度为k且有2^k-1个结点,为满二叉树
(5) 完全二叉树
2,二叉搜索树
左子节点总是小于或等于根节点,右子节点总是大于或等于根节点
二叉搜索树的基本操作所花费的时间与这棵树的高度成正比
3,堆
最大堆:根节点的值最大
最小堆:根节点的值最小
4,红黑树
将树中节点定义为红和黑,并通过规则确保从根节点到叶节点的最长路径不超过最短路径的两倍。
每个结点包含5个属性:color,key,left,right,p
一颗红黑树是满足下面红黑性质的二叉搜索树:
(1) 每个结点是红色的或者是黑色的
(2) 根结点是黑色的
(3) 叶子结点是黑色的
(4) 如果一个结点是红色的,那么他的两个叶子结点是黑色的
(5) 每个结点,从该结点到其后代叶节点的简单路径上,均包含相同数目的黑色结点。
二,遍历
1、先序遍历——按照“根节点-左孩子-右孩子”的顺序进行访问。
void pre_traverse(BTree pTree) { if(pTree) { printf("%c ",pTree->data); if(pTree->pLchild) pre_traverse(pTree->pLchild); if(pTree->pRchild) pre_traverse(pTree->pRchild); } }
2、中序遍历——按照“左孩子-根节点-右孩子”的顺序进行访问。
void in_traverse(BTree pTree) { if(pTree) { if(pTree->pLchild) in_traverse(pTree->pLchild); printf("%c ",pTree->data); if(pTree->pRchild) in_traverse(pTree->pRchild); } }
3、后序遍历——按照“左孩子-右孩子-根节点”的顺序进行访问。
void beh_traverse(BTree pTree) { if(pTree) { if(pTree->pLchild) beh_traverse(pTree->pLchild); if(pTree->pRchild) beh_traverse(pTree->pRchild); printf("%c ",pTree->data); }
}
2,循环
1、前序遍历的循环实现
void pre_traverse(BTree pTree) { PSTACK stack = create_stack(); //创建一个空栈 BTree node_pop; //用来保存出栈节点 BTree pCur = pTree; //定义用来指向当前访问的节点的指针 //直到当前节点pCur为NULL且栈空时,循环结束 while(pCur || !is_empty(stack)) { //从根节点开始,输出当前节点,并将其入栈, //同时置其左孩子为当前节点,直至其没有左孩子,及当前节点为NULL printf("%c ", pCur->data); push_stack(stack,pCur); pCur = pCur->pLchild; //如果当前节点pCur为NULL且栈不空,则将栈顶节点出栈, //同时置其右孩子为当前节点,循环判断,直至pCur不为空 while(!pCur && !is_empty(stack)) { pCur = getTop(stack); pop_stack(stack,&node_pop); pCur = pCur->pRchild; } } }
2、中序遍历的循环实现
void in_traverse(BTree pTree) { PSTACK stack = create_stack(); //创建一个空栈 BTree node_pop; //用来保存出栈节点 BTree pCur = pTree; //定义指向当前访问的节点的指针 //直到当前节点pCur为NULL且栈空时,循环结束 while(pCur || !is_empty(stack)) { if(pCur->pLchild) { //如果pCur的左孩子不为空,则将其入栈,并置其左孩子为当前节点 push_stack(stack,pCur); pCur = pCur->pLchild; } else { //如果pCur的左孩子为空,则输出pCur节点,并将其右孩子设为当前节点,看其是否为空 printf("%c ", pCur->data); pCur = pCur->pRchild; //如果为空,且栈不空,则将栈顶节点出栈,并输出该节点, //同时将它的右孩子设为当前节点,继续判断,直到当前节点不为空 while(!pCur && !is_empty(stack)) { pCur = getTop(stack); printf("%c ",pCur->data); pop_stack(stack,&node_pop); pCur = pCur->pRchild; } } } }
3、后序遍历的非递归实现
这个复杂一些
对于任一节点P,
1)先将节点P入栈;
2)若P不存在左孩子和右孩子,或者P存在左孩子或右孩子,但左右孩子已经被输出,则可以直接输出节点P,并将其出栈,将出栈节点P标记为上一个输出的节点,再将此时的栈顶结点设为当前节点;
3)若不满足2)中的条件,则将P的右孩子和左孩子依次入栈,当前节点重新置为栈顶结点,之后重复操作2);
4)直到栈空,遍历结束。
void beh_traverse(BTree pTree) { PSTACK stack = create_stack(); //创建一个空栈 BTree node_pop; //用来保存出栈的节点 BTree pCur; //定义指针,指向当前节点 BTree pPre = NULL; //定义指针,指向上一各访问的节点 //先将树的根节点入栈 push_stack(stack,pTree); //直到栈空时,结束循环 while(!is_empty(stack)) { pCur = getTop(stack); //当前节点置为栈顶节点 if((pCur->pLchild==NULL && pCur->pRchild==NULL) || (pPre!=NULL && (pCur->pLchild==pPre || pCur->pRchild==pPre))) { //如果当前节点没有左右孩子,或者有左孩子或有孩子,但已经被访问输出, //则直接输出该节点,将其出栈,将其设为上一个访问的节点 printf("%c ", pCur->data); pop_stack(stack,&node_pop); pPre = pCur; } else { //如果不满足上面两种情况,则将其右孩子左孩子依次入栈 if(pCur->pRchild != NULL) push_stack(stack,pCur->pRchild); if(pCur->pLchild != NULL) push_stack(stack,pCur->pLchild); } } }