二叉树递归实现

二叉树递归实现比较符合树的特点,也较容易理解,代码也较为简单。接下来进行图文详解。

C代码下载
C++代码下载
java代码下载
( 备用地址下载)

导航
1.创建二叉树
2.前序遍历二叉树
3.中序遍历二叉树
4.后序遍历二叉树
5.层次遍历二叉树
6.计算二叉树深度
7.计算二叉树双分支节点数
8.计算二叉树单分支节点数
9.计算二叉树叶子节点数
10.添加节点
11.查找二叉树中的节点

注:有一些重复的代码且多的就不重复贴出来了,需要的可以点上面的链接去下载。







一、创建二叉树

按照前序遍历来创建,给定一个串,其规则是空格代表空节点
例如给定串:ABC D EF G ;
创建步骤如下:

这个回溯就相当于出栈一样,当条件不满足了,就return,然后系统维护的栈就会执行出栈操作,这样就相当于回溯了。由此可以推出非递归的实现方式。

1.C语言实现

/*
* function              创建二叉树(前序创建)
* param     PBTree*     root(二级指针)
* param     char* s     根据给定字符串创建
* param     int i       指示当前s的下标
* return                无
*/
void CreateBTree(PBTree* root, char* s, bool newTree)
{
    static int i = -1;      //s下标
    if (newTree) i = -1;    //因为i是静态变量,所以只能手动修改它值,如果是新的二叉树则重置它的值
    ++i;
    //如果s为空,即二叉树为空,若s=""或者到达末尾s[i]都等于'\0',这里空节点用空格表示
    if (!s || s[i] == '\0' || s[i] == ' ')
        (*root) = NULL;
    else
    {
        *root = (PBTree)malloc(sizeof(_binary_tree));   //创建节点
        (*root)->data = s[i];
        CreateBTree(&(*root)->left, s, false);          //递归创建左子树   
        CreateBTree(&(*root)->right, s, false);         //递归创建右子树
    }
}

1).为什么要用二级指针?
因为传进去当参数的根节点指针会改变,所以需要一个指针的指针来一直的指向根节点

2).为什么要用静态变量i?
因为如果i不是静态的,每次递归的时候都会重置i的值。

3).为什么要加个bool标志?
因为i是静态的,只能手动去修改它的值,而只有当新创建一棵树的时候才需要重置,递归的时候是不需要的。

2.C++实现

/*
* function              创建二叉树
* param     Node**      二级指针
* param     string      s
* return                无
*/
template<typename T>
void BTree<T>::CreateBtree(Node** root, string s)
{
    ++m_i;
    //如果s为空,即二叉树为空,若s=""或者到达末尾s[i]都等于'\0',这里空节点用空格表示
    if (s.empty() || s[m_i] == ' ')
        (*root) = nullptr;
    else
    {
        *root = new Node(s[m_i]);           //创建节点
        CreateBtree(&(*root)->left, s);     //递归创建左子树   
        CreateBtree(&(*root)->right, s);    //递归创建右子树
    }
}

这里不再需要静态变量,因为已经将下标作为成员变量了,只当新创建树的时候才重置它的值。

3.java实现

/*
    * function              创建二叉树
    * param     Node        node
    * param     string      s
    * return                返回根节点
    */
    private Node createBtree(Node node, String s) {
        ++iPos;
        //如果s为空,即二叉树为空,若s=""或者到达末尾s[i]都等于'\0',这里空节点用空格表示
        if (s.isEmpty() || s.charAt(iPos) == ' ')
            return null;
        else {
            node = new Node(s.charAt(iPos));
            node.setLeft(createBtree(node.getLeft(),s));
            node.setRight(createBtree(node.getRight(),s));
            return node;
        }
    }

一开始写这个java递归创建二叉树的时候,我就卡在了,如何记录根节点的地址,因为java中没有指针这种概念,所以二级指针这种方法就不可能了,网上有一篇博客说是可以写一个类模拟二级指针,但是这样又太复杂,我不想这样写。然后转啊转的,就找到了一个写得比较好的。这个思想和C/C++的差不多,就是最后会返回根节点回去,也许你会想,它为什么会返回根节点?因为第一个进栈的就是根节点,所以最后一个出栈的就是根节点。





二、前序遍历二叉树
遍历顺序是:根节点 -> 左节点 -> 右节点
然后一个二叉树又可以分为很多子树,每一颗子树都会有根、左、右节点,所以递归的思想就很好的体现在这里了。

1.C代码

/*
* function              前序遍历
* param     PBTree      root
* return                无
*/
void PreOrder(PBTree root)
{
    if (root)
    {
        printf("%c", root->data);
        PreOrder(root->left);
        PreOrder(root->right);
    }
}

2.C++代码

/*
* function              前序遍历
* param     Node*       root
* return                无
*/
template<typename T>
void BTree<T>::PreOrder(Node* root)
{
    if (root)
    {
        cout << root->data << " ";
        PreOrder(root->left);
        PreOrder(root->right);
    }
}

3.java代码

    /*
    * function              前序遍历
    * param     Node        root
    * return                无
    */
    private void preOrder(Node root) {
        if (root != null) {
            System.out.print(root.data + " ");
            preOrder(root.left);
            preOrder(root.right);
        }
    }





三、中序遍历二叉树
遍历顺序是:左节点 -> 根节点 -> 右节点

1.C代码

/*
* function              中序遍历
* param     PBTree      root
* return                无
*/
void InOrder(PBTree root)
{
    if (root)
    {
        InOrder(root->left);
        printf("%c", root->data);
        InOrder(root->right);
    }
}

2.C++代码

/*
* function              中序遍历
* param     Node*       root
* return                无
*/
template<typename T>
void BTree<T>::InOrder(Node* root)
{
    if (root)
    {
        InOrder(root->left);
        cout << root->data << " ";
        InOrder(root->right);
    }
}

3.java代码

    /*
    * function              中序遍历
    * param     Node        root
    * return                无
    */
    private void inOrder(Node root) {
        if (root != null) {
            inOrder(root.left);
            System.out.print(root.data + " ");
            inOrder(root.right);
        }
    }





四、后序遍历二叉树
遍历顺序:左节点 -> 右节点 -> 根节点

1.C代码

/*
* function              后序遍历
* param     PBTree      root
* return                无
*/
void PostOrder(PBTree root)
{
    if (root)
    {
        PostOrder(root->left);
        PostOrder(root->right);
        printf("%c", root->data);
    }
}

2.C++代码

/*
* function              后序遍历
* param     Node*       root
* return                无
*/
template<typename T>
void BTree<T>::PostOrder(Node* root)
{
    if (root)
    {
        PostOrder(root->left);
        PostOrder(root->right);
        cout << root->data << " ";
    }
}

3.java代码

/*
    * function              后序遍历
    * param     Node        root
    * return                无
    */
    private void postOrder(Node root) {
        if (root != null) {
            postOrder(root.left);
            postOrder(root.right);
            System.out.print(root.data + " ");
        }
    }





五、层次遍历二叉树
从左到右遍历,这个需要一个辅助函数,这个函数负责遍历指定层数,然后主函数负责循环遍历层数。这个缺点就是,每一次的遍历都要从根节点开始,当层数很大的时候,遍历就会非常消耗时间。

/*
* function              层次遍历辅助函数
* param     PBTree      root
* param     int         level
* return                无
*/
void PrintNodeAtLevel(PBTree root, int level)
{
    // 空树或层级不合理
    if (NULL == root || level < 1)
        return;

    if (1 == level) //相当于输出根节点,因为每一个节点都可以左为子树的根节点
    {
        printf("%c", root->data);
        return;
    }

    // 左子树的 level - 1 级
    PrintNodeAtLevel(root->left, level - 1);

    // 右子树的 level - 1 级
    PrintNodeAtLevel(root->right, level - 1);
}





六、计算二叉树深度
先计算左子树深度,再计算右子树深度,然后返回较大的那个+1

/*
* function              计算二叉树深度
* param     PBTree      root
* return                返回二叉树深度
*/
int BTreeDepth(PBTree root)
{
    if (!root)
        return 0;
    else
    {
        int lDepth = BTreeDepth(root->left);   //递归计算左子树的深度
        int rDepth = BTreeDepth(root->right);  //递归计算右子树的深度
                                            //返回较深的那个+1
        if (lDepth >= rDepth)
            return lDepth + 1;
        else
            return rDepth + 1;
    }
}





七、计算二叉树双分支节点数
用前序遍历的方法,一个个节点遍历过去,如果该节点不为空,则判断它是否有左孩子和有孩子,如果两个都有,则count + 1

/*
* function              计算二叉树双分支节点数
* param     PBTree      root
* return                返回二叉树双分支节点数
*/
int GetN2(PBTree root, bool newTree)
{
    static int count = 0;
    if (newTree) count = 0;
    if (root == NULL)   //如果二叉树为空,则返回0
        return 0;
    else
    {
        if (root->left && root->right)  //当该节点有两个分支的时候+1
            ++count;
        GetN2(root->left, false);    //遍历左子树
        GetN2(root->right, false);   //遍历右子树
    }
    return count;
}





八、计算二叉树单分支节点数
和计算双分支节点的方法一样,只需要把判断语句改一下即可

if ((root->left && !root->right) || (!root->left && root->right))  //当该节点仅且只有一个分支的时候+1
            ++count;





九、计算二叉树叶子节点数
这个就简单了,有一个公式: n0 = n2 + 1

/*
* function              计算二叉树终端节点数
* param     PBTree      root
* return                二叉树终端节点数
*/
int GetN0(PBTree root)
{
    return GetN2(root, true) + 1; //计算公式n0 = n2 + 1;
}





十、添加节点
可以用前序、中序、后序、层次遍历的方法来添加,前三个的缺点很明显,最后添加后可能会退化成一个长长的单链表。所以这里采用层次遍历的方法添加,一层层扫描,遇到空节点就添加在它那里。

/*
* function              添加节点的辅助函数
* param     PBTree      root
* param     int         level
* param     char        ch
* param     bool*       标记是否添加成功
* return                无
*/
void LevelAdd(PBTree root, int level,char ch,bool* bAdd)
{
    //用来标记新的节点是否已经添加,如果添加了就退出了,避免重复添加
    if (*bAdd)return;
    //如果该节点为空,则可以将ch赋值给该节点
    if (!root->left || !root->right)
    {
        PBTree node = (PBTree)malloc(sizeof(_binary_tree)); //创建节点
        node->data = ch;
        node->left = NULL;
        node->right = NULL;
        if(!root->left)
            root->left = node;
        else
            root->right = node;
        *bAdd = true;
        return;
    }
    //层级不合理
    if (level < 1)
        return;
    //递归结束条件
    if (1 == level)
        return;

    // 左子树的 level - 1 级
    LevelAdd(root->left, level - 1, ch, bAdd);

    // 右子树的 level - 1 级
    LevelAdd(root->right, level - 1, ch, bAdd);
}

/*
* function              添加节点值(添加的位置是不确定的)
* param     PBTree      root
* param     char        ch
* return                无
*/
void AddValue(PBTree root,char ch)
{
    //采用层次遍历的办法,一层层扫描,若有空的地方,则添加到该地方
    if (NULL == root) //如果此时二叉树为空,则创建根节点
    {
        root = (PBTree)malloc(sizeof(_binary_tree));    //创建节点
        root->data = ch;
        root->left = NULL;
        root->right = NULL;
        return;
    }   
    int depth = BTreeDepth(root);

    bool bAdd = false;   //标记是否添加成功,避免重复添加
    for (int i = 1; i <= depth; i++)
    {
        if (bAdd)   //如果已经添加成功,则退出
            break;
        LevelAdd(root, i, ch, &bAdd);
    }       
}

十一、查找二叉树中的节点

用前序遍历的方法查找

/*
* function              查找该值
* param     PBTree      root
* param     char        ch
* param     bool        标志是否是第一次查找,如果是第一次要将标志重置,因为静态变量要手动重置它的值
* return                若存在则返回true,否则返回false
*/
bool Search(PBTree root, char ch,bool first)
{
    static bool bFind = false;
    if (first) bFind = false;
    if (bFind)return true;    //如果已经找到了就不需要继续查找了
    //利用前序遍历来查找
    if (root)
    {
        if (root->data == ch)
            bFind = true;
        Search(root->left, ch, false);
        Search(root->right, ch, false);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_18297675/article/details/68498645