408数据结构学习笔记——二叉树的遍历和线索二叉树

目录

1.二叉树的先中后序遍历

1.1.先中后序遍历的基本概念

1.2.先中后序遍历的代码实现

1.2.1.递归

1.2.2.非递归

2.二叉树的层次遍历

2.1.算法思想

2.2.代码实现

3.由遍历序列构造二叉树

3.1.前序+中序确定一个二叉树

3.2.后序+中序确定一个二叉树

3.3.层序+中序确定一个二叉树

4.线索二叉树

4.1.线索二叉树的概念

4.2.中序线索化

4.3.先序线索化

4.4.后序线索化

4.5.找前驱/后继

4.5.1.中序线索树找后继

4.5.2.中序线索树找前驱

4.5.3.先序搜索树找后继

4.5.4.先序搜索树找前驱

4.5.5.后序搜索树找后继

4.5.6.后序搜索树找前驱

5.王道课后题​


1.二叉树的先中后序遍历

1.1.先中后序遍历的基本概念

  1. 先序遍历:根→左→右:ABDECFG
  2. 中序遍历:左→根→右:DBEAFCG
  3. 后序遍历:左→右→根:DEBFGCA
  4. 可以先按遍历的顺序写出每次递归的子树的根左右结点,然后依次按结点添加下一次递归的根左右结点,直到访问全部结点(逐层展开)
    1. 先序遍历:根左右→根(根左右)右→ 根(根左右)(根左右)
    2. 中序遍历:左根右→(左根右)根→(左根右)根(左根右)
    3. 后序遍历:左右根→(左右根)右根→(左右根)(左右根)根

1.2.先中后序遍历的代码实现

1.2.1.递归

typedef struct BiTNode{
    elemType value;
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

//先序遍历
void preOrder(BiTree T){
    if(T != NULL){
        visit(T);    //访问根节点
        preOrder(T->lchild);    //递归遍历左子树
        preOrder(T->rchild);    //递归遍历右子树
    }
}

//中序遍历
void inOrder(BiTree T){
    if(T != NULL){
        inOrder(T->lchild);    //递归遍历左子树
        visit(T);    //访问根节点
        inOrder(T->rchild);    //递归遍历右子树
    }
}

//后序遍历
void postOrder(BiTree T){
    if(T != NULL){
        postOrder(T->lchild);    //递归遍历左子树
        postOrder(T->rchild);    //递归遍历右子树
        visit(T);    //访问根节点
    }
}

  1. #126为第126行代码,#127为第127行代码
  2. preOrder(A)→访问A→preOrder(A)入栈#126,进入preOrder(B)
  3. 访问B→preOrder(B)入栈#126,进入preOrder(D)
  4. 访问D→左子树为空→preOrder(D)入栈#127,进入preOrder(G)
  5. 访问G→左子树空→右子树空→返回preOrder(D)#127→返回preOrder(B)#126→preOrder(B)#127入栈,进入preOrder(E)
  6. 访问E→左子树为空→右子树为空→返回preOrder(B)#127→返回preOrder(A)#126→preOrder(A)#127入栈,进入preOrder(C)
  7. 访问C→preOrder(C)#126入栈,进入preOrder(F)
  8. 访问F→左子树为空→右子树为空→返回preOrder(C)#126→右子树为空→返回preOrder(A)#127→结束遍历

1.2.2.非递归

后序遍历非递归:栈中剩余元素都是栈顶元素的双亲结点,此特性可求根到结点的路径和两个节点的最近公共结点

#define MAXSIZE 100

typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    Elemtype value;
}BiTNode, *BiTree;

typedef struct Stack{
    BiTNode data[MAXSIZE];
    int top;
}Stack;

//先序遍历
void PreOrder(BiTree T){
    Stack S;
    InitStack(S);
    BiTNode *p = T;
    //当p不存在并且栈空时结束循环
    while(p || !(IsEmpty(S)){
        if (p) {
            //先序遍历先访问p再入队
            visit(p);
            push(S, p);
            p = p->lchild;
        }
        else{
            pop(S, p);
            p = p->rchild);
        }
    }//while
}

//中序遍历
void InOrder(BiTree T){
    Stack S;
    InitStack(S);
    BiTNode *p = T;
    while(p || !(IsEmpty(S)){
        if (p){
            push(S, p);
            p = p->lchild;
        }
        else{
            //中序遍历出栈后访问p
            pop(S, p);
            visit(p);
            p = p->rchild;
        }
    }//while
}

//后序遍历
void PostOrder(BiTree T){
    Stack S;
    InitStack(S);
    BiTNode *p = T, *r = NULL;
    while(p || !(IsEmpty(S)){
        //p存在,则入栈p,进入p的左子树
        if (p){
            push(S, p);
            p = p->lchild;
        }
        //p不存在
        else{
            //查看栈顶元素
            GetTop(S, p);
            //栈顶元素的右孩子未访问过,则将右孩子入栈,并且进入其左子树
            if (p->rchild && p->rchild != r) {
                p = p->rchild;
                push(S, p);
                p = p->lchild;
            }
            //栈顶元素的右孩子访问过,则出栈并访问栈顶元素,并用r进行标记
            //访问后p置空再次进入else循环
            else {
                pop(S, p);
                visit(p);
                r = p;
                p = NULL;
            }
        }//else
    }//while
}

2.二叉树的层次遍历

2.1.算法思想

  1. 初始化一个队列
  2. 将根节点入队
  3. 若队不空,则队头结点出队,访问该节点,并且将其左右孩子入队
  4. 重复3直至队列为空

2.2.代码实现

typedef struct BiTNode{
    elemType value;
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

typedef struct linkNode{
    //存指针
    BiTNode *data;
    struct linkNode *next;
}linkNode;

typedeft struct linkQueue{
    linkNode *front, *rear;
}linkQueue;

//层次遍历
void levelOrder(BiTree T){
    linkQueue Q;
    initQueue(Q);    //初始化队列
    BiTree p;
    enQueue(Q, T);    //T入队
    while(!isEmpty(Q)){
        deQueue(Q, p);    //p出队
        visit(p);    //访问p
        if (p->lchild) enQueue(Q, p->lchild);    //p的左孩子入队
        if (p->rchild) enQueue(Q, p->rchild);    //p的右孩子入队
    }
}
        

3.由遍历序列构造二叉树

中序+前序/后序/层序可以确定一个二叉树

3.1.前序+中序确定一个二叉树

  1. 前序序列:D A E F B C H G I
  2. 中序序列:E A F D H C B G I
  3. 前序序列第一个访问的是根节点→D是根节点→左(EAF)根(D)右(HCBGI)
  4. 前序序列中EAF第一个访问的是根节点→A是EAF根节点→左(E)根(A)右(F)
  5. 前序序列中HCBGI第一个访问的是根节点→B是HCBGI根节点→左(HC)根(B)右(GI)
  6. 前序序列中HC第一个访问的是根节点→C是HC的根节点→左(H)根(C)
  7. 前序序列中GI第一个访问的是根节点→G是GI的根节点→根(G)右(I)

3.2.后序+中序确定一个二叉树

  1. 后序序列:E F A H C I G B D
  2. 中序序列:E A F D H C B G I
  3. 后序序列中最后访问的是根节点→D是根节点→左(EAF)根(D)右(HCBGI)
  4. 后序序列中EAF最后访问的是根节点→A是EAF根节点→左(E)根(A)右(F)
  5. 后序序列中HCBGI最后访问的是根节点→B是HCBGI根节点→左(HC)根(B)右(GI)
  6. 后序序列中HC最后访问的是根节点→C是HC根节点→左(H)根(C)
  7. 后序序列中GI最后访问的是根节点→G是GI根节点→根(I)右(G)

3.3.层序+中序确定一个二叉树

  1. 层次序列:D A B E F C G H I
  2. 中序序列:E A F D H C B G I
  3. 层次序列中第一个访问的是根节点→D是根节点→左(EAF)根(D)右(HCBGI)
  4. 层次序列中EAF第一个访问的是根节点→A是EAF根节点→左(E)根(A)右(F)
  5. 层次序列中HCBGI第一个访问的是根节点→B是HCBGI根节点→左(HC)根(B)右(GI)
  6. 层次序列中HC第一个访问的是根节点→C是HC的根节点→左(H)根(C)
  7. 层次序列中GI第一个访问的是根节点→G是GI的根节点→根(G)右(I)

4.线索二叉树

4.1.线索二叉树的概念

二叉树使用链式存储时会有n + 1个空指针未使用,可以将其指向该结点的前驱和后继,使得二叉树具有线性特性,从而方便遍历

规定线索二叉树中,相比传统二叉树,多两个标志位——ltag和rtag。tag用来标记指针指向的是左右孩子还是前驱后继

typedef struct threadNode{
    elemType data;
    struct threadNode *lchild, *rchild;
    int ltag, rtag;
}threadNode, *threarTree;

4.2.中序线索化

typedef struct threadNode{
    elemType data;
    struct threadNode *lchild, *rchild;
    int ltag, rtag;
}threadNode, *threadTree;

//全局变量pre,指向当前当前结点的前驱结点
threadNode *pre = NULL;

void inThread(threadTree &T){
    if (T){
        inThread(T->lchild);
        visit(T);
        inThread(T->rchild);
    }
}

void visit(threadNode *q){
    //如果p节点左孩子不存在,则左孩子指向该节点的前驱结点
    if (!q->lchild) {
        q->lchild = pre;
        q->ltag = 1;
    }
    //如果pre结点存在并且它的右子树为空
    if (pre && !pre.rchild){
        pre->rchild = q;
        pre->rtag = 1;
    }
    pre = q;
}

void createInThread(threadTree T){
    pre = NULL;
    if(T) {
        inThread(T);
        //如果最后一个结点的右孩子为空,则改标记位
        if (pre->rchild == NULL) pre->rtag = 1;
    }
}
    

4.3.先序线索化

需要注意,如果不对ltag进行判断,则会进入死循环

typedef struct threadNode{
    struct threadNode *lchild, *rchild;
    elemType data;
    int ltag, rtag;
}threadNode, *threadTree;

threadNode pre = NULL;

void preThread(threadTree T){
    visit(T);
    //防止进入死循环
    if (ltag == 0) preThread(T->lchild);
    preThread(T->rchild);
}

void visit(threadTree *p){
    if (p->lchild == NULL) {
        p->lchild = pre;
        p->ltag = 1;
    }
    if (pre && pre->rchild == NULL) {
        pre->rchild = p;
        pre->rtag = 1;
    }
    pre = p;
}

void createPreThread(threadTree T){
    pre = NULL;
    if (T){
        preThread(T);
        if (pre->rchild  == NULL) pre->rtag = 1;
    }
}
        

4.4.后序线索化

typedef struct threadNode{
    struct threadNode *lchild, *rchild;
    elemType data;
    int ltag, rtag;
}threadNode, *threadTree;

threadNode *pre = NULL;

void postThread(threadTree T){
    postThread(T->lchild);
    postThread(T->rchild);
    visit(T);
}

void visit(threadNode *p){
    if (p->lchild == NULL){
        p->lchild = pre;
        p->ltag = 1;
    }
    if (pre && pre->rchild == NULL){
        pre->rchild = p;
        pre->rtag = 1;
    }
    pre = p;
}

void createPostThread(threadTree T){
    pre = NULL;
    if (T) {
        postThread(T);
        if (pre->rchild == NULL) pre->rtag = 1;
    }
}

4.5.找前驱/后继

4.5.1.中序线索树找后继

  1. p->rtag = 1,next = p->rchild
  2. p->rtag = 0,next = 右子树的最左节点
//找到中序遍历的第一个节点
threadNode *firstNode(threadNode *p){
    //找到最左下的结点,并非一定是叶子结点
    //有可能是左空右不空
    while(p->ltag == 0) p = p->lchild;
    return p;
}

//找当前节点的后继结点
threadNode *nextNode(threadNode *p){
    //rtag = 0说明有右子树,继续寻找右子树中的最左节点
    if (p->rtag == 0) return firstNode(p->rchild);
    //rtag = 1说明右子树为指向的是后继结点
    else return p->rchild;
}

//中序遍历
void InOrder(ThreadNode *T){
    for (threadNode *p = firstNode(T); p != NULL; p = nextNode(p)) visit(p);
}

4.5.2.中序线索树找前驱

  1. p->ltag = 1,pre = p->lchild
  2. p->ltag = 0,pre = 左子树的最右结点
//找到最后一个结点
ThreadNode *LastNode(ThreadNode *p){
    while(p->rtag == 0) p = p->rchild;
    return p;
}

//找前驱结点
ThreadNode *PreNode(ThreadNode *p){
    if (p->rtag == 0) return LastNode(p->lchild);
    else return p->rchild;
}

//中序遍历
void InOrder(ThreadTree T){
    for (ThreadNode *p = LastNode(T); p != NULL; p = preNode(p)) visit(p);
}

4.5.3.先序搜索树找后继

  1. p->ltag = 1,next = p->rchild
  2. p->ltag = 0
    1. 左子树不空,next = 左孩子
    2. 左子树空,右子树不空,next = 右孩子

4.5.4.先序搜索树找前驱

  1. p->ltag = 1,pre = p->lchild
  2. p->ltag = 0
    1. 如果没有parent指针,则无法找到前驱
    2. 有parent指针:
      1. p是左孩子:pre = p->parent
      2. p是右孩子,但左兄弟为空:pre = p->parent
      3. p是右孩子,且有左兄弟:pre = 左兄弟子树的最后一个遍历的结点
      4. p是根节点:pre = NULL

4.5.5.后序搜索树找后继

  1. p->rtag = 1,next = p->rchild
  2. p->rtag = 0
    1. 如果没有parent指针,则无法找到后继
    2. 有parent指针:
      1. p是右孩子:next = p->parent
      2. p是左孩子,且没有右兄弟:next = p->parent
      3. p是左孩子,且有右兄弟:next = 右兄弟子树中第一个被后序遍历的结点
      4. p是根节点,p没有后继

4.5.6.后序搜索树找前驱

  1. p->ltag = 1,pre = p->lchild
  2. p->ltag = 0,p必有左子树
    1. p有右子树,pre = p->rchild
    2. p没有右子树,pre = p->lchild

5.王道课后题

 每层只有一个节点

 只有根节点

typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    Elemtype value;
}BiTNode, *BiTree;

typedef struct Stack{
    int top;
    Elemtype data[MAXSIZE];
}Stack;

void PostOrder(BiTree T){
    Stack S;
    InitStack(S);    //初始化栈
    BiTNode *p = T, *r = NULL;
    //栈空并且p为NULL结束循环
    while (p || !(isEmpty(S)){
        //p非空
        if (p) {
            push(S, p);    //p入栈
            p = p->lchild;    //p指向p的左孩子
        }
        //p空
        else {
            //p右孩子存在并且r不指向p的右孩子(p的右孩子未遍历过)
            if (p->rchild && p->rchild != r){
                //p的右孩子入栈,并且p指向其右孩子的左孩子
                p = p->rchild;
                push(S,p);
                p = p->lchild;
            }
            //p的右孩子不存在 或 p右孩子存在,并且r指向p的右孩子
            else {
                //出栈栈顶元素并且访问它
                pop(S, p);
                visit(p);
                //r指向p,并且p置空(防止进入死循环)
                r = p;
                p = NULL;
            }
        }//else
    }//while
}  

typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    Elemtype value;
}BiTNode, *BiTree;

typedef struct LNode{
    struct LNode *next;
    BiTNode *data;
}LNode;

typedef struct Queue{
    int rear, front;
}LinkQueue;

void LevelOrder(BiTree T){
    BiTNode *p;
    LinkQueue Q;
    InitQueue(Q);    //初始化队列
    if (T == NULL) return; //空树返回
    EnQueue(T);    //根节点入队
    //循环直到队空
    while (!(isEmpty(Q)) {
        //队头元素出队,并用p指向它,进行访问
        DeQueue(Q, p);
        visit(p);
        //按顺序入栈p的右孩子和左孩子
        if (p->rchild) EnQueue(p->rchild);
        if (p->lchild) EnQueue(p->lchild);
    }
}

//非递归方式
typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    Elemtype value;
}BiTNode, *BiTree;

typedef struct LNode{
    BiTNode *data;
    struct LNode *next;
}LNode;

typedef struct Queue{
    int front, rear;
}

int getDepth(BiTree T) {
    //树空,返回高度0
    if (!T) return 0;
    //last指向每层最后一个元素,第一层指向根节点
    int depth = 0, front = -1, rear = -1, last = 0;
    BiTree Q[MAXSIZE];
    //根节点入队
    Q[++rear] = T;
    BiTNode* p = NULL;
    //循环直到队空
    while (front < rear) {
        //出队队头元素,并且p指向它
        p = Q[++front];
        //入队p的左右孩子(如果存在)
        if (p->lchild) Q[++rear] = p->lchild;
        if (p->rchild) Q[++rear] = p->rchild;
        //遍历到本层最后一个元素
        if (last == front) {
            //深度+1
            depth++;
            //last指向下层最后一个元素
            last = rear;
        }
    }
    return depth;
}

//递归方式
typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    Elemtype value;
}BiTNode, *BiTree;

int deep(BiTree T) {
    int ldeep, rdeep;
    //执行到叶子结点,返回高度0
    if (T == NULL) return 0;
    else {
        ldeep = deep(T->lchild);
        rdeep = deep(T->rchild);
        return (ldeep > rdeep ? ldeep : rdeep) + 1;
    }
}

typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    Elemtype value;
}BiTNode, *BiTree;

//m1和m2分别指向PreOrder的第一个元素和最后一个元素的数组下标
//n1和n2分别指向InOrder的第一个元素和最后一个元素的数组下标
BiTree CreateTree(int PreOrder[], int InOrder[], int m1, int m2, int n1, int n2) {
    //新建结点,并传入数据
    BiTree T = (BiTNode*)malloc(sizeof(BiTNode));
    T->data = PreOrder[m1];
    //在中序序列中找到当前的树的根节点
    int i = n1;
    while (InOrder[i] != PreOrder[m1]) i++;
    //leftLen标记当前节点的左子树个数
    int leftLen = i - n1;
    //rightLen标记当前节点的右子树个数
    int rightLen = n2 - i;
    //左子树个数>0,传入相应数组内容,递归建立左子树,
    if (leftLen) T->lchild = CreateTree(PreOrder, InOrder, m1 + 1, m1 + leftLen, n1 , n1 + leftLen - 1);
    //左子树=0,左子树置空
    else T->lchild = NULL;
    //右子树同理
    if (rightLen) T->rchild = CreateTree(PreOrder, InOrder, m1 + leftLen + 1, m2, n2 - rightLen + 1, n2);
    else T->rchild = NULL;
    return T;
}

typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    Elemtype value;
}BiTNode, *BiTree;

typedef struct LNode{
    BiTNode *data;
    struct LNode *next;
}LNode; 

typedef struct Queue{
    int rear, front;
}Queue;

bool IsComplete(BiTree T){
    BiTNode *p;
    Queue Q;
    InitQueue(Q);    //初始化队列
    if (!T) return ture;    //空树也是完全二叉树
    EnQueue(Q, T);    //根节点入队
    while( !(IsEmpty)) {    //循环直到队空
        DeQueue(Q,p);    //出队队头元素
        if(p) {    //p存在
            EnQueue(p, T->lchild);
            EnQueue(p, T->rchild);
        }
        //p不存在
        else {
            //出队队头元素,并用p指向它
            while( !(IsEmpty)){
                DeQueue(Q, p);
                //p存在则返回false
                if (p) return false;
            }
        }//else
    }//while
    return true;
}

//递归实现
typedef struct BiTNode{
    BiTNode *lchild, rchild;
    Elemtype data;
}BiTNode, *BiTree;

//法1
int count = 0;
int FullChildNode(BiTree T){
    if (T){
        if (T->lchild && T->rchild) count++;
        FullChildNode(T->lchild);
        FullChildNode(T->rchild);
    }
}

//法2
int FullChildNode(BiTree T){
    //空树,返回0
    if (T == NULL) return 0;
    //双孩子都存在
    if (T->lchild && T->rchild) {
        return FullChildNode(T->lchild) + FullChildNode(T->rchild) + 1;
    }
    //双孩子中至少有一个不存在
    else return FullChildNode(T->lchild) + FullChildNode(T->rchild);
}


//层次遍历
typedef struct BiTNode{
    BiTNode *lchild, *rchild;
    elemtype value;
}BiTNode, *BiTree;

typedef struct LNode{
    BiTNode *data;
    struct LNode *next;
}LNode;

typedef struct Queue{
    int  rear, front;
}Queue;

int FullChildNode(BiTree T) {
    //空树则返回0
    if (T == null) return 0;
    int count = 0;
    BiTNode* p = NULL;
    Queue Q;
    //初始化队列
    InitQueue(Q); 
    //根节点入队
    EnQueue(Q, T);
    //层次遍历二叉树
    while (!(IsEmpty(Q)) {
        //队头元素出队,并用p指向它
        DeQueue(Q, p);
        //左孩子和右孩子都存在,count++
        if (p->rchild && p->lchild) count++;
        //左孩子存在,左孩子入队
        if (p->lchild) EnQueue(Q, p->lchild);
        //右孩子存在,右孩子入队
        if (p->rchild) EnQueue(Q, p->rchild);
    }
    return count;
}

typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    Elemtype value;
}BiTNode, *BiTree;

BiTree exchangeNode(BiTree T) {
    //temp保存右子树
    BiTNode* temp = T->rchild;
    //将右子树指针指向左子树
    T->rchild = T->lchild;
    //右子树指向temp
    T->lchild = temp;
    return T;
}

BiTree InOrder(BiTree T) {
    //当前节点为空,返回
    if (!T) return T;
    //交换当前节点的左右子树
    T = exchangeNode(T);
    //访问左子树
    InOrder(T->lchild);
    //访问右子树
    InOrder(T->rchild);
    return T;
}

//递归    
typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    Elemtype value;
}BiTNode, *BiTree;

int i = 0;    //全局变量i用于记录遍历第几个结点

char ch;    //ch用来标记是否找到该结点

int PreOrderK(BiTree T, int k) {
    //当前结点为NULL,返回'#'
    if (!T) return '#';
    //当前结点非空,已遍历结点数+1
    i++;
    //扫描到第k个结点,返回当前结点的值
    if (i == k) return T->value;
    //遍历左子树
    ch = PreOrderK(T->lchild, k);
    //如果ch的值不是'#',则说明找到了第k个结点的值,递归出口
    //ch的值是'#',则继续遍历右子树
    if (ch != '#') return ch;
    //遍历右子树
    else ch = PreOrderK(T->rchild, k);
    //递归出口
    return ch;
}

    
//非递归
#define MAXSIZE 100
typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    Elemtype value;
}BiTNode, *BiTree;

typedef struct Stack{
    int top = -1;
    BiTNode data[MAXSIZE];
}Stack;

Elemtpye PreOrderK(BiTree T, int k) {
    Stack S;
    InitStack(S);
    BiTNode* p = T;
    int count = 0;
    while (p || !(IsEmpty(S))) {
        if (p) {
            //count标记当前是第几个元素
            count++;
            //count = k返回当前p的值
            if (k == count) return p->value;
            //将p入栈
            push(S, p);
            p = p->lchild;
        }//if
        else {
            pop(S, p);
            p = p->rchild;
        }//else
    }//while
    return 0;    //0表示当前树中没有k
}

删除以它为根的子树:使用后序遍历,后序遍历的左右根的访问顺序可以很好的满足删除操作的需求

遍历树并查找值x的结点:使用层次遍历从上至下的访问树

typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    Elemtype value;
}BiTNode, *BiTree;

typedef struct LNode{
    struct LNode *next;
    BiTNode *data;
}LNode;

typedef struct Queue{
    LNode *rear, *front;
}Queue;

//释放T及其左右子树
bool DeleteX(BiTree T) {
    //T不为空时
    if (T) {
        //T的左子树存在,删除左子树
        if (T->lchild) DeleteX(T->lchild);
        //T的右子树存在,删除右子树
        if (T->rchild) DeleteX(T->rchild);
        //释放T的存储空间
        free(T);
    }
    return true;
}

//遍历树查找x
BiTNode* LevelOrder_SearchX(BiTree &T, int x) {
    //树空,返回NULL
    if (!T) return NULL;
    //根节点为x,删除根节点及其子树,返回NULL
    if (T->value == x) {
        DeleteX(T);
        return NULL;
    }
    Queue Q;
    InitQueue(Q);
    //根节点入队
    EnQueue(Q, T);
    BiTNode* p = NULL;
    //循环直到队空
    while (!(IsEmpty(Q))) {
        //出队队首元素,并用p指向它
        DeQueue(Q, p);
        if (p->lchild){
            //当前结点的左孩子为x,删除左子树,并将左孩子的指针设为NULL
            if (p->lchild->value == x) {
                DeleteX(p->lchild);
                p->lchild = NULL;
            }
            //当前结点的左孩子不为x,将左孩子入队
            else EnQueue(Q, p->lchild);
        }
        if (p->rchild) {
            //当前结点的右孩子为x,删除右子树,并将右孩子的指针设为NULL
            if (p->rchild->value == x) {
                DeleteX(p->rchild);
                p->rchild = NULL;
            }
            //当前结点的右孩子不为x,将右孩子入队
            else EnQueue(Q, p->rchild);
        }
    }//while     
}

使用先序遍历的特性(根左右的访问顺序),如果当前结点的值为x,更改标记位置,依次返回双亲结点,直到根节点

typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    elemtype value;
}BiTNode, *BiTree;

//全局变量res,用于标记是否找到x
int res = 0;

void InOrderSearchX(BiTree T, int x) {
    //当前结点为空,返回上一个结点
    if (!T) return;
    //当前结点的值为value,标记位res的值改为1,返回上一个结点
    if (T->value == x) {
        res = 1;
        return;
    }
    //左孩子存在,进入左孩子
    if (T->lchild) InOrderSearchX(T->lchild, x);
    //标记位为1,输出当前结点的值,返回上一个结点
    if (res) {
        cout << T->value << endl;
        return;
    }
    //右孩子存在,进入右孩子
    if (T->rchild) InOrderSearchX(T->rchild, x);
    //标记位为1,输出当前结点的值,返回上一个结点
    if (res) cout << T->value << endl;
    return;
}

非递归后序遍历,访问到值为x的结点时,栈中所有元素即为该结点的根节点,依次出栈并输出

#define MAXSIZE 100

typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    Elemtype value;
}BiTNode, *BiTree;

typedef struct Stack{
    BiTNode[MAXSIZE];
    int top;
}Stack;

void PostOrderSearchX(BiTree T){
    Stack S;
    InitStack(S);
    BiTNode *p = T, *r = NULL;
    while(p || !(IsEmpty(S)){
        if (p) {
            push(S, p);
            p = p->lchild;
        }
        else{
            //查看栈顶元素
            GetTop(S, p);
            //栈顶元素的值为x时
            if (p->value == x) {
                //弹出栈顶元素
                pop(S, p);
                //栈中的剩余元素都是值为x的元素的双亲结点,依次弹出并输出,return结束函数
                while(!(IsEmpty(S){
                    pop(S, p);
                    cout << p->value;
                    return;
                }
            if (p->rchild && p->rchild != r){
                p = p->rchild;
                push(S, p);
                p = p->lchild;
            }
            else {
                pop(S, p);
                visit(p);
                r = p;
                p = NULL;
            }
        }//else
    }//while
} 

找公共节点和根到结点的路径用后序遍历的非递归算法 

#define MAXSIZE 100

typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    Elemtype info;
}BiTNode, *BiTree;

typedef struct Stack{
    int top;
    BiTNode data[MAXSIZE];
}Stack;

BiTNode *CommonBiTNode(BiTree root, BiTNode *p, BiTNode *q){
    Stack S, A;
    int tag = 0;
    InitStack(S);
    InitStack(A);
    BiTNode *r = NULL, *t = root, *commonNode = NULL;
    while(t || !(IsEmpty(S)){
        if (t){
            push(S, t);
            t = t->lchild;
        }
        else{
            //查看栈顶元素
            GetTop(S, t);
            //t是pq任意一个时,tag+1
            if (t == p || t == q) tag++;
            //当tag = 1时,将S中的元素自栈低到栈顶逐一压入到A中
            if (tag == 1) {
                for (int i = 0; A.top <= S.top; i++) push(A, S.data[i]);
            }
            //当tag = 2时,遍历两个栈找到相同元素
            if (tag == 2){
                //top指向的栈顶元素是p或者q,因此从top - 1去开始遍历
                for (int i = S.top - 1; i >= 0; i--){
                    for (int j = A.top - 1; j >=; j--){
                        if (S.data[i] == A.data[j]) return A.data[top];
                    }
            }
            if (t->rchild && t->rchild != r) {
                t = t->rchild;
                push(S, t);
                t = t->lchild;
            }
            else {
                pop(S, t);
                r = t;
                t = NULL;
            }
        }//else
    }//while
    //没有公共结点
    return NULL;
}

1.用层次遍历的特性,由上至下逐一遍历各层的元素,并且用cur标记已经扫描的结点个数

2.层次遍历,双队列

typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    Elemtype data;
}BiTNode, *BiTree;

typedef struct LNode{
    struct LNode *next;
    BiTNode *data;
}LNode;

typedef struct Queue{
    LNode *rear, *front;
}Queue;

//记录每层的最后一个结点
int BiTreeWidth(BiTree T) {
    //空树,宽度为0
    if (!T) return 0;
    //temp标记是否队首元素是当前层最后一个元素
    //cur记录当前层的节点个数,max记录最大值
    int width = 0, cur = 0, max = 0, temp = 0;
    BiTNode* p = NULL;
    Queue Q;
    InitQueue(Q);
    EnQueue(Q, T);
    //last指针标记每层的最后一个元素
    LNode* last = Q.rear;
    while (!IsEmpty(Q)) {
        //队头元素为last
        if (Q.front->next == last) {
            cur++;
            //更新最大值
            if (max < cur) max = cur;
            cur = 0;
            //更改temp值,表示已经到当前层最后一个元素
            temp = 1;
        }
        else {
            cur++;
            temp = 0;
        }
        DeQueue(Q, p);
        if (p->lchild) EnQueue(Q, p->lchild);
        if (p->rchild) EnQueue(Q, p->rchild);
        //更改last为下一层最后一个元素
        if (temp) last = Q.rear;
    }//while
    return max;
}

//双队列
int BiTreeWidth(BiTree T) {
    if (!T) return 0;
    Queue A, B;
    InitQueue(A);
    InitQueue(B);
    int level = 1, width = 0, max = 0;
    EnQueue(A, T);
    LNode* last = A.front->next;
    LNode* temp = NULL;
    BiTNode* p = NULL;
    while (!IsEmpty(A) || !IsEmpty(B)) {
        //奇数层A出队,B入队
        if (level % 2) {
            DeQueue(A, p);
            if (p->lchild) EnQueue(B, p->lchild);
            if (p->rchild) EnQueue(B, p->rchild);
        }
        //偶数层A入队,B出队
        else {
            DeQueue(B, p);
            if (p->lchild) EnQueue(A, p->lchild);
            if (p->rchild) EnQueue(A, p->rchild);
        }
        //A空
        if (IsEmpty(A)) {
            //层数+1
            level++;
            //宽度重置
            width = 0;
            //temp指向B的头结点
            temp = B.front;
            //循环遍历队列B,每遍历到一个元素宽度+1
            while (temp->next) {
                temp = temp->next;
                width++;
            }
            //更新最大值
            if (max < width) max = width;
        }
        //B空同理
        if (IsEmpty(B)) {
            level++;
            width = 0;
            temp = A.front;
            while (temp->next) {
                temp = temp->next;
                width++;
            }
            if (max < width) max = width;
        }
    }//while
    return max;
}

普通二叉树无法通过先序序列和后序序列判断二叉树,但是满二叉树可以

满二叉树中,先序序列的第一个结点和后序序列的第二个结点都是根节点

先序序列除第一个节点的左半边是左子树,右半边是右子树;后序序列除最后一个节点外,左半边是左子树,右半边是右子树

typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    Elemtype value;
}BiTNode, *BiTree;

BiTNode* post[MAXSIZE];

BiTNode* PreToPost(BiTNode* pre[], BiTNode *post[], int s1,int e1, int s2, int e2) {
    int half;
    if (e1 >= s1) {
        //找到当前的中间结点
        half = (e1 - s1) / 2;
        //满二叉树中的先序遍历的第一个结点和后序遍历的最后一个结点相同,都是根节点
        post[e2] = pre[s1];
        //处理左子树
        PreToPost(pre, post, s1 + 1, s1 + half, s2, s2 + half - 1);
        //处理右子树
        PreToPost(pre, post, s1 + half + 1, e1, s2 + half, e2 - 1);
    }
}

从左到右,满足先序遍历的特性

typedef struct BiTNode{
    struct BiTNode *lchild, *rchild;
    elemtype value;
}BiTNode, *BiTree;

typedef struct LNode{
    struct LNode *next;
    BiTNode *data;
}LNode;

LinkList L;
LNode* last;
LinkList InOrder(BiTree T, LinkList &L, LNode *&last) {
    //第一次进入函数,为头结点申请空间,并且用last指向它
    if (!L) {
        L = (LNode*)malloc(sizeof(LNode));
        last = L;
    }
    //设置标记位tag
    int tag = 0;
    //有左孩子则进入左孩子,没有则tag+1
    if (T->lchild) InOrder(T->lchild, L, last);
    else tag++;
    //有左孩子则进入左孩子,没有则tag+1
    if (T->rchild) InOrder(T->rchild, L, last);
    else tag++;
    //左右孩子都不存在,则用尾插法将当前节点插入链表中
    if (tag == 2) {
        LNode* temp = (LNode*)malloc(sizeof(LNode));
        temp->data = T;
        temp->next = NULL;
        last->next = temp;
        last = temp;
    }
    return L;
}

//层次遍历,非递归
bool IsSymmetry(BiTree A, BiTree B) {
    //两树都空,返回true
    if (!A && !B) return true;
    //一树空一树不空,返回false
    else if (A && !B) return false;
    else if (!A && B) return false;
    BiTNode* p = NULL, * q = NULL;
    Queue a, b;
    InitQueue(a);
    InitQueue(b);
    //分别入队根节点
    EnQueue(a, A);
    EnQueue(b, B);
    //ab栈任意一个空则结束循环
    while (!IsEmpty(a) && !IsEmpty(b)) {
        //出队队首元素
        DeQueue(a, p);
        DeQueue(b, q);
        //pq都有左孩子则左孩子进队
        if (p->lchild && q->lchild) {
            EnQueue(a, p->lchild);
            EnQueue(b, q->lchild);
        }
        //p有左孩子,q没有左孩子,返回false
        else if (p->lchild && q->lchild == NULL) return false;
        //p没有左孩子,q有左孩子,返回false
        else if (p->lchild == NULL && q->lchild) return false;
        //右孩子同理
        if (p->rchild && q->rchild) {
            EnQueue(a, p->rchild);
            EnQueue(b, q->rchild);
        }
        else if (p->rchild && q->rchild == NULL) return false;
        else if (p->rchild == NULL && q->rchild) return false;
    }
    //结束循环时,栈都空则返回true,有栈不空则返回false
    if (IsEmpty(a) && IsEmpty(b)) return true;
    else return false;
}
//先序遍历递归实现
bool IsSymmetry(BiTree A, BiTree B) {
    int left, right;
    //当前结点都空,返回true
    if (!A && !B) return true;
    //当前结点一空一不空,返回false
    else if (!A || !B) return false;
    //当前结点都存在,进入左右子树
    else {
        left = IsSymmetry(A->lchild, B->lchild);
        right = IsSymmetry(A->rchild, B->rchild);
        return left && right;
    }
}

typedef struct ThreadNode {
    struct ThreadNode* lchild, * rchild;
    elemtype value;
    int ltag, rtag;
}ThreadNode, *ThreadTree;

ThreadNode *SearchPre(ThreadTree T, ThreadNode *& p) {
    ThreadNode* q = NULL;
    //p右子树存在,右子树就是p的前驱结点
    if (p->rtag == 0) q = p->rchild;
    //p右子树不存在,左子树存在,左子树就是p的前驱结点
    else if (p->ltag == 0) q = p->lchild;
    //中序线索二叉树的第一个结点的左子树为空
    else if (p->lchild == NULL) q = NULL;
    else {
        //顺着左线索寻找中序遍历的前驱结点
        while (p->lchild && p->ltag == 1) p = p->lchild;
        //p结点的祖先的左孩子是其前驱
        if (p->ltag == 0) q = p->lchild;
        //单边树,没有前驱
        else q = NULL;
    }
    return q;
}

//层次遍历
typedef struct BiTNode {
    struct BiTNode* lchild, * rchild;
    int weight;
}BiTNode, *BiTree;

typedef struct Queue {
    BiTNode *data[MAXSIZE];
    int top;
}Queue;

int GetWpl(BiTree T) {
    Queue Q;
    InitQueue(Q);
    BiTNode* p = T;
    int level = 0, wpl = 0;
    EnQueue(Q, p);
    int last = Q.rear;
    while (!IsEmpty(Q)) {
        DeQueue(Q, p);
        if (p->lchild) EnQueue(Q, p->lchild);
        if (p->rchild) EnQueue(Q, p->rchild);
        //遍历到当前层的最后一个结点,并且不是队列中最后一个结点
        if (last == Q.front && Q.front != Q.rear) {
            level++;
            last = Q.rear;
        }
        //当前结点左右孩子都不存在,计算其wpl值
        if (!p->lchild && !p->rchild) wpl = wpl + level * p->value;
    }
    return wpl;
}

void InOrder(BiTree T) {
    //空结点
    if (!T) return;
    //叶子结点,输出data
    else if (!T->lchild && !T->rchild) cout << T->data;
    //分支节点
    else {
        //输出左括号
        cout << "(";
        //T左孩子存在,进入左孩子
        if (T->lchild) InOrder(T->lchild);
        //输出当前结点data
        cout << T->data;
        //T右孩子存在,进入右孩子
        if (T->rchild) InOrder(T->rchild);
        //输出右括号
        cout << ")";
    }
}

  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值