《数据结构高分笔记》树与二叉树

二叉树的递归遍历(前中后)
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);//剪枝,只有在左子树没有找到的情况下才会去右子树寻找
     }
}
中序遍历输出第K个结点值

其实如果寻找结点最好使用先序遍历,因为中序和后序都在第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;//下一次会将右子树根节点放入栈中,开始遍历右子树
          }
     }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值