typedef struct BTNode{
char data;
struct BTNode *lchild, *rchild;
}BTNode;
//previous order
void preorder(BTNode *p){
if(p == NULL) return ;
visit(p);
preorder(p->lchild);
preorder(p->rchild);
}
//in order
void inorder(BTNode *p){
if(p == NULL) return;
inorder(p->lchild);
visit(p);
inorder(p->rchild);
}
//post order
void postorder(BTNode *p){
if(p == NULL) return;
postorder(p->lchild);
postorder(p->rchild);
visit(p);
}
树型表达式的特点是操作数都在叶结点,而操作符都在分支结点;所以当一个节点的左右孩子都为空时结束递归,此时返回这个结点的操作数;类似于后序遍历;
using namespace std;
const int maxn = 100;
typedef struct BTNode{
char data;
struct BTNode *lchild, *rchild;
}BTNode;
BTNode tree[maxn];
//根据操作符计算值,输入参数为两个操作数,最后为一个操作符
int op(int a, int b, char c){
if(c == '+') return a + b;
if(c == '-') return a - b;
if(c == '*') return a * b;
if(c == '/' && b != 0) return a / b;
else return 0;
}
//计算子树的表达式值,参数是根结点指针,返回结果
int comp(BTNode *root){
//因为数字都在叶子结点,所以递归到叶子结点就返回,而不是叶子结点的下一层
if(root->lchild == NULL && root->rchild == NULL) return root->data - '0';
int left = comp(root->lchild);
int right = comp(root->rchild);
return op(left, right, root->data);
}
//根据层次顺序建树,空节点用空格表示
BTNode* create(int n){
queue<BTNode*> Q;
BTNode *root = NULL, *top;
char c;
for(int i = 1; i <= n; i ++){
//构造结点
scanf("%c", &c);
BTNode *p = NULL;
//如果不是空节点-,就需要分配内存,放入队中
if(c != ' '){
p = (BTNode*)malloc(sizeof(BTNode));
p->data = c, p->lchild = NULL, p->rchild = NULL;
Q.push(p);
top = Q.front();//对于每一个有效结点,都需要找他的父亲结点
if(i == 1) root = p;//如果是第一个,需要设置为返回值
else if(i % 2 == 0) top->lchild = p;//如果不是第一个且是奇数,说明是左孩子
else if(i % 2 == 1) top->rchild = p;
}
if(i % 2 == 1 && i != 1) Q.pop();//奇数才需要删除
}
return root;
}
int main()
{
int n;
scanf("%d", &n);
getchar();
BTNode *root = create(n);
int ans = comp(root);
printf("%d\n", ans);
return 0;
}
采用带返回值的递归实现,分别求出左子树和右子树的深度,返回二者的最大值 + 1,作为当前结点的深度;递归结束的条件为访问到叶子结点的下一层;
int getDepth(BTNode *root){
if(root == NULL) return 0;//如果是叶子结点的下一层,返回零
int left = getDepth(root->lchild);
int right = getDepth(root->rchild);
return (left > right ? left : right) + 1;//返回左右子树中深度较大值
}
先序遍历的方式查找(其他方法也可以),先判断当前结点值是否等于目标值,如果等于,则表明找到,终止结束;否则去左子树查找,为了减少不必要的循环,左子树没有找到的时候才会去右子树查找(即适当剪枝);
//先序查找某一节点是否出现在树中
void Search(BTNode *root, BTNode *&q, char k){
if(root == NULL) return;
if(root->data == k) q = root;
else{
Search(root->lchild);
if(q == NULL) Search(root->rchild);//剪枝,只有在左子树没有找到的情况下才会去右子树寻找
}
}
其实如果寻找结点最好使用先序遍历,因为中序和后序都在第2次或第3次经过结点时才会判断是否为目标值,效率不高;
回到本题,如果中序遍历,只要把判断语句放到中间即可,但是不能剪枝,因为一定是在遍历右子树的时候找到目标值;
void trave(BTNode *root, int k){
if(root == NULL) return;
trave(root->lchild, k);
n ++;
if(n == k) {
printf("%c", root->data);
return ;
}
//没有必要剪枝回溯,因为根据中序遍历的特点,遍历左子树会一直到最下层,肯定是遍历右子树的过程中找到目标值
//但是先序遍历的话就需要剪枝了
trave(root->rchild, k);
}
借助循环队列实现,每次取出队首元素并输出;如果队首元素存在左右孩子,则入队;一直迭代直到队列为空;
void level(BTNode * root){
if(root == NULL) return;
//initialize queue
int rear = 0, front = 0;//队首,队尾指针,队首指针指向第一个元素的前一个位置,队尾指针指向最后一个元素
BTNode *queue[maxn], *top = NULL;
//push root into queue
rear = (rear + 1) % maxn;
queue[rear] = root;
//undo until queue is empty
while(rear != front){
//pop up front elem
front = (top + 1) % maxn;
top = queue[front];
cout << top->data;
//push left child when left child is not empty
if(top->lchild) {
rear = (rear + 1) % maxn;
queue[rear] = top->lchild;
}
//push right child when right child is not empty
if(top->rchild) {
rear = (rear + 1) % maxn;
queue[rear] = top->rchild;
}
}
}
借助一个栈来模拟,先将根节点入栈,每次循环都将栈顶元素弹出,代表先 遍历此元素;接着查看右子树是否为空,不空则入栈;再查看左子树是否为空,不空则入栈;(因为栈先进的后出,所以右子树元素要先入栈);
void preorderNonrecursion(BTNode *root){
if(root == NULL) return;
//initialize stack
BTNode *stack[maxn];
BTNode *p = NULL;
int top = -1;
//push root into stack
stack[++top] = root;
while(top != -1){
p = stack[top--];//every loop must have elem is popped,but push elem when it has child
Visit(p);
//first push right child , then push left child
if(p->rchild) stack[top++] = p->rchild;
if(p->lchild) stack[top++] = p->lchild;
}
}
核心是搞清楚什么时候出栈,当左子树为空时才会出栈;然后访问栈顶结点,继续将右子树根节点压入栈;
void inorderNonrecusion(BTNode *root){
if(root == NULL) return;
BTNode *stack[maxn];
int top = -1;
BTNode *p = root;
//需要考虑栈空了,但是还没有遍历右子树的情况
while(top != -1 || p != NULL){
//重复多次将节点进栈,直到某个节点不存在左孩子,即找到最左边节点
while(p != NULL){
stack[++top] = p;
p = p->lchild;
}
//每次找到最左边节点后就需要弹出一个元素,即使这个结点是个空结点也需要弹出
if(top != -1){
p = stack[top--];
Visit(p);
p = p->rchild;//下一次会将右子树根节点放入栈中,开始遍历右子树
}
}
}