數據結構(十四) -- C語言版 -- 樹 - 二叉樹的葉子節點、深度、拷貝等

零、讀前說明

  • 本文中所有設計的代碼均通過測試,並且在功能性方面均實現應有的功能。
  • 設計的代碼並非全部公開,部分無關緊要代碼並沒有貼出來。
  • 如果你也對此感興趣、也想測試源碼的話,可以私聊我,非常歡迎一起探討學習。
  • 由於時間、水平、精力有限,文中難免會出現不準確、甚至錯誤的地方,也很歡迎大佬看見的話批評指正。
  • 嘻嘻。。。。 。。。。。。。。收!

一、求二叉樹的葉子節點個數

  首先,我們需要明確什麼是葉子節點?二叉樹的葉子節點是 既沒有左子樹又沒有右子樹的特殊的節點

  詳細的二叉樹的相關的知識點可以參考下面兩個文章:

    數據結構(十) – C語言版 – 樹 - 基礎知識

    數據結構(十一) – C語言版 – 樹 - 二叉樹基本概念

  二叉樹的遍歷是操作二叉樹的基礎,二叉樹的很多特性可以通過遍歷二叉樹的過程得到,在實際應用中,統計二叉樹的葉子節點的個數是比較常見的一種操作。

  二叉樹的葉子節點個數的操作可以使用先序遍歷,中序遍歷,後序遍歷中的任何一種,只需要在將訪問操作變成判斷該節點是否爲葉子節點(既不存在左孩子、也不存在右孩子的節點)

    如果是葉子節點,那麼累加和加一
    如果不是葉子節點,則繼續遞歸判斷

  本文章中使用的是先序的方式進行統計,有興趣的小夥伴可以試一下其他的方式(其實就是將判斷的部分代碼移動一下就行了)。下面是代碼。

/**
 * 功 能:
 *      求出二叉樹的葉子節點的個數
 * 參 數:
 *      root:要操作的樹
 *      num :葉子節點的個數的返回值
 *            注意:不能爲NULL!
 * 返回值:
 *      成功:葉子節點的個數
 *      失敗:-1 
 **/
int CounterBiTreeLeaves(BiTNode *root, int *num)
{
    int tmpnum = 0;
    if (num == NULL)
        return -1;

    if (root)
    {
        // 判斷是否爲葉子節點
        if (root->lchild == NULL && root->rchild == NULL)
        {
            (*num)++;
        }
        // 遞歸判斷左孩子
        if (root->lchild)
        {
            CounterBiTreeLeaves(root->lchild, num);
        }
        // 遞歸判斷右孩子
        if (root->rchild)
        {
            CounterBiTreeLeaves(root->rchild, num);
        }
    }
    else
    {
        return -1;
    }

    tmpnum = *num;
    return tmpnum;
}

二、求二叉樹的深度/高度

  首先我們還是需要明確什麼是樹的高度。

  樹的深度/高度:樹中所有節點的最大層次,也稱爲樹的高度

  求二叉樹的深度/高度的操作相比而言後序遍歷操作比較符合人們求二叉樹的高度的方式。

    首先,分別求出左子樹和右子樹的高度
    然後,在左子樹和右子樹的高度的基礎上求出輸的高度。

樹的高度 = MAX[ 左子樹高度,右子樹高度 ] + 1

  本文中的代碼如下。

/**
 * 功 能:
 *      求出二叉樹的深度/高度
 * 參 數:
 *      root:要操作的樹
 * 返回值:
 *      樹的深度/高度 
 **/
int CounterBiTreeDepth(BiTNode *root)
{
    int leftDepth = 0;
    int rightDepth = 0;
    int value = 0;

    if (root == NULL)
    {
        return value;
    }
    // 左子樹的高度
    leftDepth = CounterBiTreeDepth(root->lchild);
    // 右子樹的高度
    rightDepth = CounterBiTreeDepth(root->rchild);
    // 左子樹的高度 或者 右子樹高度 + 1
    value = 1 + (leftDepth > rightDepth ? leftDepth : rightDepth);

    return value;
}

三、拷貝二叉樹

  拷貝一個二叉樹,其實與創建一個二叉樹的過程是一致的,也是將每一個節點一個一個的進行復制,也就是一個遞歸遍歷創建的過程。

  首先,判斷是否爲空樹
    1、如果是空樹,則直接遞歸結束返回爲空
    2、如果不爲空樹
      1)判斷是否存在左子樹,存在則遞歸複製左子樹並返回節點,否則返回爲空
      2)判斷是否存在右子樹,並且遞歸複製右子樹並返回節點,否則返回爲空
      3)申請一個新結點空間,複製當前根結點。
      4)然後將返回的左指針指向當前節點的左子樹,右指針指向右子樹
      5)將返回的節點的值複製給當前節點

  本文中的代碼如下。

/**
 * 功 能:
 *      複製一個二叉樹
 * 參 數:
 *      tree:要複製的樹
 * 返回值:
 *      成功:複製之後的樹的根節點
 *      失敗:NULL
 **/
BiTNode *CopyBiTree(BiTNode *tree)
{
    BiTNode *newNode = NULL;
    BiTNode *newLeft = NULL;
    BiTNode *newRight = NULL;

    if (tree == NULL)
        return NULL;

    // 遞歸複製左子樹
    if (tree->lchild != NULL)
    {
        newLeft = CopyBiTree(tree->lchild);
    }
    else
    {
        newLeft = NULL;
    }
    // 遞歸複製右子樹
    if (tree->rchild != NULL)
    {
        newRight = CopyBiTree(tree->rchild);
    }
    else
    {
        newRight = NULL;
    }
    // malloc 根節點
    newNode = (BiTNode *)malloc(sizeof(BiTNode));
    if (newNode == NULL)
        return NULL;

    // 左指針指向左子樹,右指針指向右子樹
    newNode->lchild = newLeft;
    newNode->rchild = newRight;

    newNode->data = tree->data;

    return newNode;
}

四、測試案例

  前面已經說明二叉樹三種比較常見的操作,均使用二叉樹的遞歸的方法實現,對於理解二叉樹的相關也能起到一定的作用吧,對於理解遞歸也是比較好的案例,下面就對上面三種操作方式進行驗證測試。

  下面簡單的編寫一個測試demo,測試一下創建過程,主要的 main.c 文件中的內容如下。

#include "../src/biTree/biTree.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern func_BiTree fBiTree;

int main(int argc, const char *argv[])
{
    int ret = 0;
    printf("請按照先序輸入二叉樹(空用#表示):");
    BiTNode *tree = fBiTree.create();
    if (tree != NULL)
    {
        printf("二叉樹創建成功!\n");
    }
    else
    {
        printf("二叉樹創建出現異常!\n");
        ret = -1;
        goto ERROR_END;
    }
    printf("先序遍歷輸出:");
    fBiTree.prevOrder(tree);
    putchar(10);

    printf("中序遍歷輸出:");
    fBiTree.inOrder(tree);
    putchar(10);

    printf("後序遍歷輸出:");
    fBiTree.postOrder(tree);
    putchar(10);

    printf("層序遍歷輸出:");
    fBiTree.levelOrder(tree);
    putchar(10);

    int leaf = 0;
    fBiTree.leafNum(tree, &leaf);
    printf("樹的葉子節點的個數爲:%d\n", leaf);

    int depth = fBiTree.depth(tree);
    printf("樹的深度/高度爲:%d\n", depth);

    BiTNode *copyTree = fBiTree.copy(tree);
    printf("copy樹的先序遍歷輸出:");
    fBiTree.prevOrder(copyTree);
    putchar(10);

    printf("copy樹的中序遍歷輸出:");
    fBiTree.inOrder(copyTree);
    putchar(10);

    printf("copy樹的後序遍歷輸出:");
    fBiTree.postOrder(copyTree);
    putchar(10);

    printf("copy樹的層序遍歷輸出:");
    fBiTree.levelOrder(copyTree);
    putchar(10);

ERROR_END:
    // 釋放二叉樹
    fBiTree.release(tree);
    // 釋放二叉樹
    fBiTree.release(copyTree);

    printf("system exit with return code %d\n", ret);

    return 0;
}

  下面爲工程文件的結構,使用cmake進行工程管理與編譯。


$ tree biTree-cases/
biTree-cases/
├── CMakeLists.txt
├── README.md
├── image
│   └── image.jpg
├── main
│   └── main.c
├── runtime
└── src
    └── biTree
        ├── biTree.c
        └── biTree.h

5 directories, 6 files

  然後創建並編譯、運行工程,詳細效果如下圖所示。
  
在這裏插入圖片描述

圖4.1 測試案例的效果

  

  爲了方便測試,本測試中輸入的二叉樹的結構如下圖所示。
  
在這裏插入圖片描述

圖4.2 測試的二叉樹的結構

  

  好啦,本文就這樣比較水的完成了,但是總歸是在我了一段時間纔算完成,總結寫作不易,如果你喜歡這篇文章或者對你有用,請動動你發財的小手手幫忙點個贊,當然關注一波那就更好了,好啦,就到這兒了,麼麼噠(*  ̄3)(ε ̄ *)。
在這裏插入圖片描述

上一篇:數據結構(十三) – C語言版 – 樹 - 二叉樹的遍歷(遞歸、非遞歸)
下一篇:數據結構(十五) – C語言版 – 樹 - 二叉樹的操作進階之創建、插入、刪除、查詢、銷燬

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章