1 二叉树结构定义
//二叉树树结点定义
typedef struct BTNode{
char val;
struct BTNode *lChild, *rChild;
}BTNode, *BTree;
2 创建二叉树
//使用前序遍历的顺序存储结构创建二叉树
BTree CreatBTreePre(){
char ch;
scanf("%c", &ch);
BTree root;
if (ch == '#')
root = NULL;
else{
root = (BTree)malloc(sizeof(BTree));
root->val = ch;
root->lChild = CreatBTreePre();
root->rChild = CreatBTreePre();
}
return root;
}
3 前序遍历——递归
void PreOrderRe(BTree root){
if (!root)
return;
printf("%c ", root->val);
PreOrderRe(root->lChild);
PreOrderRe(root->rChild);
}
4 中序遍历——递归
void InOrderRe(BTree root){
if (!root)
return;
InOrderRe(root->lChild);
printf("%c ", root->val);
InOrderRe(root->rChild);
}
5 后序遍历——递归
void PostOrderRe(BTree root){
if (!root)
return;
PostOrderRe(root->lChild);
PostOrderRe(root->rChild);
printf("%c ", root->val);
}
6 前序遍历——非递归
//1 沿着根的左孩子依次访问并入栈,直至左孩子为空
//2 栈顶元素出栈:
// 2.1 若其右孩子为空,继续执行
// 2.2 若右孩子不空,将右子树执行 1
void PreOrderNotRe(BTree root){
Stack *s = (Stack *)malloc(sizeof(Stack));
InitStack(s);
BTree ptr = root; //ptr 为工作指针
while (ptr || !isEmpty(s)){
if (ptr){ //只要左子树不为空,一直左走到头,访问结点并入栈
printf("%c ", ptr->val);
Push(s, ptr);
ptr = ptr->lChild;
} else{
//左结点走到头,出栈
Pop(s, &ptr);
ptr = ptr->rChild; //工作指针转向该节点的右孩子
}
}
}
7 中序遍历——非递归
//1 沿着根的左孩子依次入栈,直至左孩子为空
//2 栈顶元素出栈并访问:
// 2.1 若其右孩子为空,继续执行
// 2.2 若右孩子不空,将右子树执行 1
void InOrderNotRe(BTree root){
Stack *s = (Stack *)malloc(sizeof(Stack));
InitStack(s);
BTree ptr = root; //ptr 为工作指针
while (ptr || !isEmpty(s)){
if (ptr){ //只要左子树不为空,一直想左走
Push(s, ptr);
ptr = ptr->lChild;
} else{
//左结点走到头,出栈并访问该结点
Pop(s, &ptr);
printf("%c ", ptr->val);
ptr = ptr->rChild; //工作指针转向该节点的右孩子
}
}
}
8 后序遍历——非递归
//1 沿着根的左孩子依次入栈,直到左孩子为空
//2 读取栈顶元素(非出栈):
// 2.1 若右孩子不空且未被访问,将右子树执行 1
// 2.2 否则,栈顶元素出栈并访问
void PostOrderNotRe(BTree root){
Stack *s = (Stack *)malloc(sizeof(Stack));
InitStack(s);
BTree ptr = root; //工作指针
BTree pre = NULL; //记录当前结点右子树是否已经被访问过
while (ptr || !isEmpty(s)){
if (ptr){
Push(s, ptr);
ptr = ptr->lChild;
} else{
getTop(s, &ptr); //读取栈顶元素
if (ptr->rChild && ptr->rChild != pre) //右子树存在且未访问
ptr = ptr->rChild; //转向右子树
else{
Pop(s, &ptr); //否则,弹出结点并访问
printf("%c ", ptr->val);
pre = ptr; //记录最近访问的结点
ptr = NULL; //节点访问完后,重置工作指针
}
}
}
}
9 层序遍历
//1 先将根节点入队,然后出队,访问出队结点
//2 若有左子树,左子树根节点入队
//3 若有右子树,右子树根节点入队
//4 然后出队,访问出队结点,如此循环...直至队列为空
void LevelOrderBTree(BTree root){
Queue *q = (Queue *)malloc(sizeof(Queue));
BTree ptr;
InitQueue(q);
JoinQueue(q, root); //根节点优先入队
while (!IsEmpty(q)){
LeaveQueue(q, &ptr); //队列不空时队头出队
printf("%c ", ptr->val);
if (ptr->lChild) //若有左子树,左子树根节点入队
JoinQueue(q, ptr->lChild);
if (ptr->rChild) //若有右子树,右子树根节点入队
JoinQueue(q, ptr->rChild);
}
}
10 前序中序还原二叉树
//前序中序重建二叉树
BTree PreAndInCreatBTree(char *preOrder, int preStart, int preEnd,
char *inOrder, int inStart, int inEnd){
if (preStart > preEnd || inStart > inEnd)
return NULL;
BTree root = (BTree)malloc(sizeof(BTree));
root->val = preOrder[preStart]; //当前子树的根节点为前序的第一个值
root->lChild = NULL;
root->rChild = NULL;
//定位到前序的根节点在中序中的位置
int index = inStart;
for (; index <= inEnd; index++)
if (inOrder[index] == preOrder[preStart])
break;
//递归创建左右子树
root->lChild = PreAndInCreatBTree(preOrder, preStart+1, preStart + index - inStart, inOrder, inStart, index - 1);
root->rChild = PreAndInCreatBTree(preOrder, preStart + index - inStart + 1, preEnd, inOrder,index + 1, inEnd);
return root;
}
11 中序后序还原二叉树
BTree InAndPostCreatBTree(char *inOrder, int inStart, int inEnd,
char *postOrder, int postStart, int postEnd){
if (inStart > inEnd || postStart > postEnd)
return NULL;
BTree root = (BTree)malloc(sizeof(BTree));
root->val = postOrder[postEnd];
root->lChild = NULL;
root->rChild = NULL;
//定位到后序的根节点在中序中的位置
int index = inStart;
for (; index <= inEnd; index++)
if (inOrder[index] == postOrder[postEnd])
break;
root->lChild = InAndPostCreatBTree(inOrder, inStart, index - 1, postOrder, postStart, postStart + index - inStart - 1);
root->rChild = InAndPostCreatBTree(inOrder, index + 1, inEnd, postOrder, postStart + index - inStart, postEnd - 1);
return root;
}
12 反向层序遍历
//写出二叉树自下而上、自右至左的层次遍历算法
//先将二叉树按照正常的层序遍历进行遍历
//将每个结点依次加入栈中,然后依次出栈即可实现反向层序遍历
void ReverseLever(BTree root){
Queue *q = (Queue*)malloc(sizeof(Queue));
Stack *s = (Stack*)malloc(sizeof(Stack));
BTree ptr;
InitStack(s);
InitQueue(q);
JoinQueue(q, root);
while (!IsEmpty(q)){
LeaveQueue(q, &ptr);
Push(s, ptr);
if (ptr->lChild)
JoinQueue(q, ptr->lChild);
if (ptr->rChild)
JoinQueue(q, ptr->rChild);
}
while (!isEmpty(s)){
BTree *dis = (BTree *)malloc(sizeof(BTree));
getTop(s, dis);
printf("%c ", (*dis)->val);
Pop(s, &root);
free(dis);
}
}
13 求树高度(深度)
//设计一个非递归算法求二叉树高度
//在层次遍历的同时,记录当前队列的最大长度即可
int GetHight(BTree root){
if (!root) return 0;
if (!root->lChild && !root->rChild) return 1;
int hight = 0;
Queue *q = (Queue*)malloc(sizeof(Queue));
InitQueue(q);
JoinQueue(q, root);
while (!IsEmpty(q)){
hight++;
//当前层的队列宽度
int index = q->rear - q->front;
BTree node = NULL;
for (int i = 0; i < index; ++i) {
LeaveQueue(q, &node);
if (node->lChild || node->rChild){
if (node->lChild)
JoinQueue(q, node->lChild);
if (node->rChild)
JoinQueue(q, node->rChild);
}
}
}
return hight;
}
14 完全二叉树的判定
//判断一个给定的二叉树是否为完全二叉树
//1 使用层序遍历为基础,进行循环判断
//2 当前结点有四种情况:
// 2.1 左右孩子都为空
// 2.2 左孩子不空,右孩子为空
// 2.3 左孩子为空,右孩子不空
// 2.4 左右孩子均不为空
//3 根据以下几种情况判断是否为完全二叉树:
// 3.1 若当前结点出现情况 2.3,直接返回 false,不可能为完全二叉树
// 3.2 若当前结点出现情况 2.4,则继续访问其他节点
// 3.3 若当前结点出现情况 2.2,则从此刻开始,所有的结点都应当是叶节点,
// 即满足情况 2.1,如出现其他情况,说明不是完全二叉树
_Bool IsCompleteBinaryTree(BTree root){
if (!root) return 1;
_Bool leaf = 0; //设置布尔变量来标记是否已经开启了状态 2.2
Queue *q = (Queue *)malloc(sizeof(Queue));
InitQueue(q);
JoinQueue(q, root);
while (!IsEmpty(q)){
BTree node = NULL;
LeaveQueue(q, &node);
//状态开启的情况下出现了非叶结点 或者 状态未开启但是出现了情况 2.3
if ((leaf && (node->lChild || node->rChild)) || (!node->lChild && node->rChild))
return 0;
if (node->lChild)
JoinQueue(q, node->lChild);
if (node->rChild)
JoinQueue(q, node->rChild);
//遇见情况 2.1 或 2.2,状态开启
if ( (node->lChild && !node->rChild) || (!node->rChild && !node->lChild))
leaf = 1;
}
return 1;
}
15 双分支结点数
//计算一棵给定二叉树中双分支结点的个数
//使用层序遍历树节点,若当前结点左右子树均存在,则计数器加一
int DoubleBranchNodeNums(BTree root){
if (!root) return 0;
if (!root->rChild && !root->lChild) return 0;
int count = 0;
Queue *q = (Queue *)malloc(sizeof(Queue));
BTree cur = NULL;
InitQueue(q);
JoinQueue(q, root);
while (!IsEmpty(q)){
LeaveQueue(q, &cur);
if (cur->lChild && cur->rChild)
count++;
if (cur->lChild)
JoinQueue(q, cur->lChild);
if (cur->rChild)
JoinQueue(q, cur->rChild);
}
return count;
}
16 左右子树交换
//将二叉树中所有结点的左右子树进行交换
void SwapLeftAndRight(BTree root){
if (root){
SwapLeftAndRight(root->lChild);
SwapLeftAndRight(root->rChild);
BTree temp = root->lChild;
root->lChild = root->rChild;
root->rChild = temp;
temp = NULL;
} else
return;
}
17 先序第 k 个结点
//求先序遍历序列中第 k 个结点的值
//使用栈进行先序遍历,在遍历的同时设置计数器计数即可
char GetKthInPreOrder(BTree root, int k){
Stack *s = (Stack *)malloc(sizeof(Stack));
InitStack(s);
BTree ptr = root;
int count = 0;
while (ptr || !isEmpty(s)){
if (ptr){
Push(s, ptr);
count++;
if (count == k)
return ptr->val;
ptr = ptr->lChild;
} else{
Pop(s, &ptr);
ptr = ptr->rChild;
}
}
}
18 删除特定结点子树
//对于树中每一个值为 x 的结点,删去以它为根节点的子树,并释放空间
_Bool DeleteSubtree(BTree root, char x, _Bool flag){
if (!root)
return 0;
else{
//当前结点为 x,更改标志位为 1,向下传递需要删除的信息
if (root->val == x)
flag = 1;
int leftRes = DeleteSubtree(root->lChild, x, flag);
int rightRes = DeleteSubtree(root->rChild, x, flag);
if (flag == 1){ //标志位为 1,说明当前结点处于应被删除的子树中
if (root->val == x) //若是 x,对上层函数传递信息,以便父节点置空
return 1;
root = NULL;
} else{
if (leftRes == 1)
root->lChild = NULL;
if (rightRes == 1)
root->rChild = NULL;
}
}
return 0;
}
19 打印特定结点的祖先
//假设二叉树中值为 x 的结点不止一个,打印所有值为 x 的结点的祖先
int XNodeAncestor(BTree root, char x){
BTree ptr = root;
if (ptr){
if (ptr->val == x)
return 1;
int left = XNodeAncestor(root->lChild, x);
int right = XNodeAncestor(root->rChild, x);
//若一个结点的子树中有要查询的结点,则该结点就是符合要求的祖先结点
if (left || right){
printf("%c ", ptr->val);
return 1;
} else
return 0;
}
return 0;
}
20 两结点的最近公共祖先
//在一棵树中,找到所给的值为 p 和 q 的结点的最近公共祖先结点
BTree CommonAncestor(BTree root, char p, char q){
//如果在某一棵子树上找到了 p,则这棵子树无序继续遍历
//因为即使这棵子树有 q,那么 p 也一定是两个结点的公共祖先
if (!root || root->val == p || root->val == q)
return root;
//按照如上规则,找到左右子树的最近公共祖先
BTree left = CommonAncestor(root->lChild, p, q);
BTree right = CommonAncestor(root->rChild, p, q);
//若左右子树分别找到了 p 和 q,则 root 就是公共祖先
if (left && right)
return root;
//一边找到了一边没找到,则找到的那个就是最近公共祖先
return left ? left : right;
}
21 满二叉由前序求后序
//假设有一棵满二叉树(结点值均不同),已知其先序序列,求其后序序列
void GetPostOrder(char *pre, int preS, int preE, char *post, int postS, int postE){
if (preS > preE)
return;
int half;
post[postE] = pre[preS];
half = (preE - preS) / 2;
GetPostOrder(pre, preS + 1, preS + half, post, postS, postS + half - 1);
GetPostOrder(pre, preS + half + 1, preE, post, postS + half, postE - 1);
}
22 叶子结点数
//计算一个二叉树的所有叶子结点数
int GetLeafNodeNums(BTree root){
int count;
if (!root)
count = 0;
else if (!root->lChild && !root->rChild)
count = 1;
else
count = GetLeafNodeNums(root->lChild) + GetLeafNodeNums(root->rChild);
return count;
}
23 总结点数
//计算一个二叉树的总结点数
int GetNodeNums(BTree root){
int count = 0;
if (!root)
return 0;
else
count = GetNodeNums(root->lChild) + GetNodeNums(root->rChild) + 1;
return count;
}
24 求树宽度
//求一棵非空二叉树的宽度(即结点数最多的那层中的结点个数)
//使用层序遍历方法进行遍历,在同时统计最长的队列长度即为最大层结点数
int GetWidth(BTree root){
Queue *q = (Queue *)malloc(sizeof(Queue));
InitQueue(q);
BTree ptr = NULL;
int length = 0;
JoinQueue(q, root);
while (!IsEmpty(q)){
if (q->rear - q->front > length)
length = q->rear - q->front;
LeaveQueue(q, &ptr);
if (ptr->lChild)
JoinQueue(q, ptr->lChild);
if (ptr->rChild)
JoinQueue(q, ptr->rChild);
}
return length;
}
25 求树高度
#define max(a, b) ((a)>= (b)) ? (a) : (b)
int GetHeight(BTree root){
return !root ? 0 : max(GetHeight(root->lChild), GetHeight(root->rChild)) + 1;
}