王道算法题(二叉树相关)

312 阅读8分钟

二叉树相关


统计二叉树中结点的个数

int Node_Count(BiTree T)
{
    if (T = NULL)
        return 0;
    else
        return Node_Count(T->lchild) + Node_Count(T->rchild) + 1;
}



计算二叉树中叶子结点的个数

int Leaf_Count(BiTree T)
{
    if (!T)
        return 0;
    if (!T->lchild && !T->rchild) // 如果二叉树左子树和右子树皆为空,说明该二叉树根节点为叶子结点,加1
        return 1;
    else
        return Leaf_Count(T->lchild) + Leaf_Count(T->rchild);
}



计算二叉树中非叶子结点的个数

int Count(BiTree T)
{
    if (!T)
        return 0;
    if (T->lchild || T->rchild)
        return Count(T->lchild) + Count(T->rchild) + 1;
}



统计二叉树的度为1的结点个数

int Count(BiTree T)
{
    if (!T)
        return 0;
    if (T->lchild && T->rchild)
        return Count(T->lchild) + Count(T->rchild);
    else
        return 1;
}



计算二叉树中度为2的结点

int Count(BiTree T)
{
    if (!T)
        return 0;
    else
    {
        if (T->lchild && T->rchild)
            return Count(T->lchild) + Count(T->rchild) + 1;
        else
            return Count(T->lchild) + Count(T->rchild);
    }
}




计算二叉树的宽度

思想:开辟一个数组count[树高],遍历每一个结点。在某层i中,每遍历到一个结点,就执行一次count[i]++。当遍历完所有的结点后,取count数组中的最大值就是树最宽的那一层的结点个数,即书的宽度。

int MAX = -1000;
int w[100];
int getWidth(BiTree T, int h)
{
    if (T == NULL)
        return *max_element(w + 1, w + h + 1); // 返回count数组中的最大值,即树的宽度
    w[h]++;                                    // 某层中每遍历到一个结点,该层的宽度就加1
    if (MAX < w[h])
        MAX = w[h];             // 发现了更宽的层
    getWidth(T->lchild, h + 1); // 继续下一层
    getWidth(T->rchild, h + 1);
}
调用方法:cout << getWidth(T, 1) << endl;



用非递归算法计算二叉树的高度

思想:使用层次遍历,利用队列指针,设置一个专门的指针last专门指向一层元素的最后一个元素。将一层中最后一个元素之前的元素依次出队,并将每个元素的孩子结点入队,那么当一层之中最后一个结点出队之时,这一层所有的孩子结点已经全部入队,即此时的尾指针只想了下一层最后一个结点之后,我们将last指向尾指针,以代表本层遍历结束 。

int Btdepth(BiTree &T)
{
    int last, level = 0;
    SqQueue Q;
    InitQueue(Q);
    BiTree p = T;
    EnQueue(Q, T);
    last = Q.rear;
    while (!IsEmpty(Q))
    {
        DeQueue(Q, p);
        if (p->lchild)
            EnQueue(Q, p->lchild);
        if (p->rchild)
            EnQueue(Q, p->rchild);
        if (Q.front == last)
        {
            level++;
            last = Q.rear;
        }
    }
    return level;
}




判断二叉树是否为平衡二叉树

思想:采用后序遍历的递归算法,判断以每一个结点为根的子树是否都满足平衡条件。

void Is_AVL(BiTree T, int balance, int h) // balance为二叉树的平衡标记,h为二叉树的高
{
    int tl = 0, tr = 0, hl = 0, hr = 0; //左、右子树的平衡标记和高度
    if (T == NULL)                      //空树,高度为0
    {
        h = 0;
        balance = 1;
    }
    else if (T->lchild == NULL && T->rchild == NULL) //仅有根结点。高度为1
    {
        h = 1;
        balance = 1;
    }
    else
    {
        Is_AVL(T->lchild, bl, hl); //递归判断左子树
        Is_AVL(T->rchild, br, hr); //递归判断右子树
        h = (hl > hr ? hl : hr) + 1;
        if (abs(hl - hr) < 2)   //若子树高度差的绝对值小于2,则看左右子树是否都平衡
            balance = bl && br; //逻辑与,即左右子树都平衡时,二叉树平衡,balance=1
        else
            balance = 0;
    }
}



判断二叉树是否为完全二叉树

思想:采用层次遍历算法。将所有结点加入队列,当遇到空结点时,查看后面是否还有非空结点。若有,则不是完全二叉树。

bool IsComplete(Bitree T)
{
    InitQueue(Q);
    if (!T)
        return 1;
    EnQueue(Q, T);
    while (!IsEmpty(Q))
    {
        DeQueue(Q, p);
        if (p)
        {
            EnQueue(p->lchild);
            EnQueue(p->rchild);
        }
        else
        {
            while (!IsEmpty(Q))
            {
                DeQueue(Q, p);
                if (p)
                    return 0;
            }
        }
    }
    return 1;
}



判断给定的二叉树是否为二叉排序树

思路:对二叉排序树来说,其中序遍历序列是一个递增有序序列。因此,对给定的二叉树进行中序遍历,若始终能保持前一个值比后一个值小,则说明该二叉树是一棵二叉排序树。

keyType predt = -32767; // predt为全局变量,保存当前结点中序前驱的值,初值为 负无穷

int JudgeBST(BiTree T)
{
    int b1, b2;
    if (T == NULL) // 空树
        return 1;
    else
    {
        b1 = JudgeBST(T->lchild);        // 判断左子树是否为二叉排序树
        if (b1 == 0 || predt >= T->data) // 若左子树返回0或前驱大于等于当前结点
            return 0;                    // 则说明不是二叉排序树
        predt = T->data;                 // 保存当前结果的关键字
        b2 = JudgeBST(T->rchild);        // 判断右子树是否为二叉排序树
        return b2;                       // 返回右子树的判断结果
    }
}



判断两棵二叉树是否相似,两棵都是空树,或只有一个结点为相似的,或者两棵树的左子树左子树是相似的,右子树右子树相似

bool similar(BiTree T1, BiTree T2)
{
    if (T1 == NULL && T2 == NULL)
        return true;
    else if (T1 == NULL || T2 == NULL)
        return false;
    else
    {
        bool l = similar(T1->lchild, T2->lchild);
        bool r = similar(T1->rchild, T2->rchild);
        return l && r;
    }
}



求两结点的最近公共结点

BiTree Fun(BiTree T, BiTree a, BiTree b)
{
    if (T == null || T == a || T == b)
        return T;

    BiTree left = Fun(T->lchild, a, b);
    BiTree right = Fun(T->rchild, a, b);

    //左和右都不为null。 说明在左子树中发现过a或b,在右子树上也发现过a或b,并且a和b在当前节点首次相遇
    if (left != null && right != null)
        return T;

    //左和右中一个不为null,另一个为null,说明不为null的节点是a或b中的一个,或者是a和b的最近祖先;直接返回;
    if (left != null)
        return left;
    if (right != null)
        return right;

    return null; //左和右均为null,没有发现a和b;
}



求编号为i和j两个结点的最近公共祖先节点

int Comm_Ancestor(SqTree T, int i, int j)
{
    if (T[i] != '#' && T[j] != '#')
    {
        while (i != j)
        {
            if (i > j)
                i = i / 2; //向上找i的祖先
            else
                j = j / 2; //向上找j的祖先
        }
        return T[i];
    }
}




从大到小输出二叉排序树中所有值不小于k的关键字

思想:因为二叉排序树的性质是【左子树 < 根 < 右子树】。所以为了从大到小输出,要先遍历右子树。再遍历根结点,最后遍历左子树。

void print(BiTree T, int k)
{
    if (T == NULL)
        return;
    if (T->rchild != NULL)
        print(T->rchild, k); //递归输出右子树结点
    if (T->data >= k)
        cout << T->data; //只输出大于等于k的结点值
    if (T->lchild != NULL)
        print(T->lchild, k); //递归输出左子树结点
}



先序遍历的第k个数

int t = 0;
void PreOrder(BiTree T)
{
    if (T)
    {
        t++;
        if (t == k)
            cout << T->data;
        PreOrder(T->lchild);
        PreOrder(T->rchild);
    }
}



在排序二叉树中寻找x所需的比较次数(求值为x的结点在二叉树中的层次)

int Search(BiTree T, char x)
{
    BiTree p = T;
    if (!p || p->data == x)
        return 1; //树为空或者树根即为x的情况
    int i = 1;
    while (p && p->data != x)
    {
        if (p->data > x)
            p = p->lchild;
        else
            p = p->rchild;
        i++;
    }
    if (!p)
        return 0; //没有找到的情况
    return i;     //成功找到
}



递归计算值为x的结点在二叉树中的层次

void getLevel(BiTree T, char x, int floor)
{
    if (T)
    {
        if (T->data == x)
            int result = floor;
        getLevel(T->lchild, x, floor + 1);
        getLevel(T->rchild, x, floor + 1);
    }
}



从右向左删除或输出二叉树中的叶子结点(将叶子结点连成单链表)

void Delete(BiTree T)
{
    BiTree p = T;
    if (!p)
        return;
    else if (!p->lchild && !p->rchild)
        cout << p->data; // free(p);  删除叶子结点
    else
    {
        Delete(p->rchild); //如果要求从左到右,就颠倒一下这两句
        Delete(p->lchild);
    }
}



删除或输出二叉树中大于x的结点

void Delete(BiTree T, int x)
{
    BiTree p = T;

    if (!p)
        return;
    else if (p->data > x)
        cout << p->data << " "; // delete(p);

    Delete(p->lchild, x);
    Delete(p->rchild, x);
}



删除二叉树中结点值小于(大于、等于)x的结点(包括其子树)

void Release(BiTree &T)
{
    if (!T)
        return;
    Release(T->lchild);
    Release(T->rchild);
    free(T);
}
void Delete_X(BiTree &T, char x)
{
    if (T == NULL)
        return;

    if (T->data >= x)
    { //自定义条件
        Release(T);
        T = NULL; //因为free只是告诉系统这块内存我们不用了而不是物
                  //理上面的释放所以我们要手动赋值NULL;
    }
    if (T != NULL)
    { //这里再次进行一次判断当T不为空时继续向下执行
        Delete_X(T->lchild, x);
        Delete_X(T->rchild, x);
    }
}



删除二叉树中,所有结点值为x的结点,删去以他为根的子树。

思想:要删除次结点,必须先删除他的左右子树,故应该使用后序遍历,根据层次遍历,找到本结点的左孩子和右孩子判断左孩子右孩子的值,若相等则删除左子树,右子树和它自己。

void del_x(BiTree &T)
{
    if (T != NULL)
    {
        del_x(T->lchild);
        del_x(T->rchild);
        free(T);
    }
}
void Search(BiTree &T, int x)
{
    SqQueue Q;
    InitQueue(Q);
    BiTree p = T;
    if (p)
    {
        if (p->data == x)
        {
            del_x(p);
            exit(0);
        }
        EnQueue(Q, T);
        while (!IsEmpty(Q))
        {
            DeQueue(Q, p);
            if (p->lchild)
                if (p->lchild->data == x)
                {
                    del_x(p->lchild);
                    p->lchild = NULL;
                }
                else
                    EnQueue(Q, p->lchild);
            if (p->rchild)
                if (p->rchild->data == x)
                {
                    del_x(p->rchild);
                    p->rchild = NULL;
                }
                else
                    EnQueue(Q, p->rchild);
        }
    }
}



求出给定二叉排序树的最大和最小值

思想:二叉排序树中最坐下结点即为最小值;最右下结点即为最大值。无需关键字比较。

int Max(BiTree T)
{
    while (T->lchild != NULL)
        T = T->lchild;
    return T->data;
}
int Min(T)
{
    while (T->rchild != NULL)
        T = T->rchild;
    return T->data;
}




二叉树中从每个叶子结点到根结点的路径

void Print_All_Path(Bitree T, char path[], int pathlen)
{
    int i;
    if (T != NULL)
    {
        path[pathlen] = T->data;
        // 如果改成【if(T->data==x)】,功能是【输出值为x的结点的祖先】
        // 如果改成【if(T==x)】,功能是【输出结点x的祖先】
        if (T->lchild == NULL &&T->rchild = NULL)
        {
            for (i = pathlen; i >= 0; i--)
                cout << path[i] << " ";
            cout << endl;
        }
        else
        {
            Print_All_Path(T->lchild, path, pathlen + 1);
            rint_All_Path(T->rchild, path, pathlen + 1);
        }
    }
}



使用递归算法进行左右结点转换

void ExChangeTree(BiTree T)
{
    BiTree temp;
    if (T != NULL)
    {
        temp = T->lchild;
        T->lchild = T->rchild; //直接交换节点地址
        T->rchild = temp;

        ExChangeTree(T->lchild);
        ExChangeTree(T->rchild);
    }
}