成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

如何使用c++圖解層序遍歷和逐層打印智能指針建造的二叉樹-創(chuàng)新互聯(lián)

本篇文章為大家展示了如何使用c++ 圖解層序遍歷和逐層打印智能指針建造的二叉樹,代碼簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。

創(chuàng)新互聯(lián)建站-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價比怒江州網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式怒江州網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋怒江州地區(qū)。費用合理售后完善,十載實體公司更值得信賴。

二叉樹是極為常見的數(shù)據(jù)結(jié)構(gòu),關(guān)于如何遍歷其中元素的文章更是數(shù)不勝數(shù)。然而大多數(shù)文章都是講解的前序/中序/后序遍歷,有關(guān)逐層打印元素的文章并不多,已有文章的講解也較為晦澀讀起來不得要領(lǐng)。本文將用形象的圖片加上清晰的代碼幫助你理解層序遍歷的實現(xiàn),同時我們使用現(xiàn)代c++提供的智能指針來簡化樹形數(shù)據(jù)結(jié)構(gòu)的資源管理。

使用智能指針構(gòu)建二叉樹

我們這里所要實現(xiàn)的是一個簡單地模擬了二叉搜索樹的二叉樹,提供符合二叉搜索樹的要求的插入功能個中序遍歷。同時我們使用shared_ptr來管理資源。

現(xiàn)在我們只實現(xiàn)insertldr兩個方法,其余方法的實現(xiàn)并不是本文所關(guān)心的內(nèi)容,不過我們會在后續(xù)的文章中逐個介紹:

struct BinaryTreeNode: public std::enable_shared_from_this<BinaryTreeNode> {
    explicit BinaryTreeNode(const int value = 0)
    : value_{value}, left{std::shared_ptr<BinaryTreeNode>{}}, right{std::shared_ptr<BinaryTreeNode>{}}
    {}

    void insert(const int value)
    {
        if (value < value_) {
            if (left) {
                left->insert(value);
            } else {
                left = std::make_shared<BinaryTreeNode>(value);
            }
        }

        if (value > value_) {
            if (right) {
                right->insert(value);
            } else {
                right = std::make_shared<BinaryTreeNode>(value);
            }
        }
    }

    // 中序遍歷
    void ldr()
    {
        if (left) {
            left->ldr();
        }

        std::cout << value_ << "\n";

        if (right) {
            right->ldr();
        }
    }

    // 分層打印
    void layer_print();

    int value_;
    // 左右子節(jié)點
    std::shared_ptr<BinaryTreeNode> left;
    std::shared_ptr<BinaryTreeNode> right;

private:
    // 層序遍歷
    std::vector<std::shared_ptr<BinaryTreeNode>> layer_contents();
};

我們的node對象繼承自enable_shared_from_this,通常這不是必須的,但是為了在層序遍歷時方便操作,我們需要從this構(gòu)造智能指針,因此這步是必須的。insert會將比root小的元素插入左子樹,比root大的插入到右子樹;ldr則是最為常規(guī)的中序遍歷,這里實現(xiàn)它是為了以常規(guī)方式查看tree中的所有元素。

值得注意的是,對于node節(jié)點我們最好使用make_shared進行創(chuàng)建,而不是將其初始化為全局/局部對象,否則在層序遍歷時會因為shared_ptr的析構(gòu)進而導(dǎo)致對象被銷毀,從而引發(fā)未定義行為。

現(xiàn)在假設(shè)我們有一組數(shù)據(jù):[3, 1, 0, 2, 5, 4, 6, 7],將第一個元素作為root,將所有數(shù)據(jù)插入我們的樹中會得到如下的一棵二叉樹:

auto root = std::make_shared<BinaryTreeNode>(3);
root->insert(1);
root->insert(0);
root->insert(2);
root->insert(5);
root->insert(4);
root->insert(6);
root->insert(7);

如何使用c++ 圖解層序遍歷和逐層打印智能指針建造的二叉樹

可以看到節(jié)點一共分成了四層,現(xiàn)在我們需要逐層打印,該怎么做呢?

層序遍歷

其實思路很簡單,我們采用廣度優(yōu)先的思路,先將節(jié)點的孩子都打印,然后再去打印子節(jié)點的孩子。

以上圖為例,我們先打印根節(jié)點的值3,然后我們再打印它的所有子節(jié)點的值,是15,然后是左右子節(jié)點的子節(jié)點,以此類推。。。。。。

說起來很簡單,但是代碼寫起來卻會遇到麻煩。我們不能簡單得像中序遍歷時那樣使用遞歸來解決問題(事實上可以用改進的遞歸算法),因為它會直接來到葉子節(jié)點處,這不是我們想要的結(jié)果。不過不要緊,我們可以借助于隊列,把子節(jié)點隊列添加到隊列末尾,然后從隊列開頭也就是根節(jié)點處遍歷,將其子節(jié)點添加進隊列,隨后再對第二個節(jié)點做同樣的操作,遇到一行結(jié)束的地方,我們使用nullptr做標(biāo)記。

先看具體的代碼:

std::vector<std::shared_ptr<BinaryTreeNode>>
BinaryTreeNode::layer_contents()
{
    std::vector<std::shared_ptr<BinaryTreeNode>> nodes;
    // 先添加根節(jié)點,根節(jié)點自己就會占用一行輸出,所以添加了作為行分隔符的nullptr
    // 因為需要保存this,所以這是我們需要繼承enable_shared_from_this是理由
    // 同樣是因為這里,當(dāng)返回的結(jié)果容器析構(gòu)時this的智能指針也會析構(gòu)
    // 如果我們使用了局部變量則this的引用計數(shù)從1減至0,導(dǎo)致對象被銷毀,而使用了make_shared創(chuàng)建的對象引用計數(shù)是從2到1,沒有問題
    nodes.push_back(shared_from_this());
    nodes.push_back(nullptr);
    // 我們使用index而不是迭代器,是因為添加元素時很可能發(fā)生迭代器失效,處理這一問題將會耗費大量精力,而index則無此煩惱
    for (int index = 0; index < nodes.size(); ++index) {
        if (!nodes[index]) {
            // 子節(jié)點打印完成或已經(jīng)遍歷到隊列末尾
            if (index == nodes.size()-1) {
                break;
            }

            nodes.push_back(nullptr); // 添加分隔符
            continue;
        }

        if (nodes[index]->left) { // 將當(dāng)前節(jié)點的子節(jié)點都添加進隊列
            nodes.push_back(nodes[index]->left);
        }
        if (nodes[index]->right) {
            nodes.push_back(nodes[index]->right);
        }
    }

    return nodes;
}

代碼本身并不復(fù)雜,重要的是其背后的思想。

算法圖解

如果你第一遍并沒有讀懂這段代碼也不要緊,下面我們有請圖解上線:

首先是循環(huán)開始時的狀態(tài),第一行的內(nèi)容已經(jīng)確定了(^代表空指針):

如何使用c++ 圖解層序遍歷和逐層打印智能指針建造的二叉樹

然后我們從首元素開始遍歷,第一個遍歷到的是root,他有兩個孩子,值分別是1和5:

如何使用c++ 圖解層序遍歷和逐層打印智能指針建造的二叉樹

接著索引值+1,這次遍歷到的是nullptr,因為不是在隊列末尾,所以我們簡單添加一個nullptr在隊列末尾,這樣第二行的節(jié)點就都在隊列中了:

如何使用c++ 圖解層序遍歷和逐層打印智能指針建造的二叉樹

隨后我們開始遍歷第二行的節(jié)點,將它們的子節(jié)點作為第三行的內(nèi)容放入隊列,最后加上一個行分隔符,以此類推:

如何使用c++ 圖解層序遍歷和逐層打印智能指針建造的二叉樹

簡單來說,就是通過隊列來緩存上一行的所有節(jié)點,然后再根據(jù)上一行的緩存得到下一行的所有節(jié)點,循環(huán)往復(fù)直到二叉樹的最后一層。當(dāng)然不只是二叉樹,其他多叉樹的層序遍歷也可以用類似的思想實現(xiàn)。

好了,知道了如何獲取每一行的內(nèi)容,我們就能逐行處理節(jié)點了:

void BinaryTreeNode::layer_print()
{
    auto nodes = layer_contents();
    for (auto iter = nodes.begin(); iter != nodes.end(); ++iter) {
        // 空指針代表一行結(jié)束,這里我們遇到空指針就輸出換行符
        if (*iter) {
            std::cout << (*iter)->value_ << " ";
        } else {
            std::cout << "\n";
        }
    }
}

如你所見,這個方法足夠簡單,我們把節(jié)點信息保存在額外的容器中是為了方便做進一步的處理,如果只是打印的話大可不必這么麻煩,不過簡單通常是有代價的。對于我們的實現(xiàn)來說,分隔符的存在簡化了我們對層級之間的區(qū)分,然而這樣會導(dǎo)致浪費至少log2(n)+1個vector的存儲空間,某些情況下可能引起性能問題,而且通過合理得使用計數(shù)變量可以避免這些額外的空間浪費。當(dāng)然具體的實現(xiàn)讀者可以自己挑戰(zhàn)一下,原理和我們上面介紹的是類似的因此就不在贅述了,也可以參考園內(nèi)其他的博客文章。

測試

最后讓我們看看完整的測試程序,記住要用make_shared創(chuàng)建root實例:

int main()
{
    auto root = std::make_shared<BinaryTreeNode>(3);
    root->insert(1);
    root->insert(0);
    root->insert(2);
    root->insert(5);
    root->insert(4);
    root->insert(6);
    root->insert(7);
    root->ldr();
    std::cout << "\n";
    root->layer_print();
}

輸出:

如何使用c++ 圖解層序遍歷和逐層打印智能指針建造的二叉樹

可以看到上半部分是中序遍歷的結(jié)果,下半部分是層序遍歷的輸出,而且是逐行打印的,不過我們沒有做縮進。所以不太美觀。

另外你可能已經(jīng)發(fā)現(xiàn)了,我們沒有寫任何有關(guān)資源釋放的代碼,沒錯,這就是智能指針的威力,只要注意資源的創(chuàng)建,剩下的事都可以放心得交給智能指針處理,我們可以把更多的精力集中在算法和功能的實現(xiàn)上。

上述內(nèi)容就是如何使用c++ 圖解層序遍歷和逐層打印智能指針建造的二叉樹,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

本文標(biāo)題:如何使用c++圖解層序遍歷和逐層打印智能指針建造的二叉樹-創(chuàng)新互聯(lián)
網(wǎng)站URL:http://jinyejixie.com/article26/coiocg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、網(wǎng)頁設(shè)計公司、自適應(yīng)網(wǎng)站靜態(tài)網(wǎng)站、外貿(mào)網(wǎng)站建設(shè)網(wǎng)站排名

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護公司
象州县| 蓝山县| 密山市| 张掖市| 如皋市| 盐边县| 台北市| 买车| 肇庆市| 平顶山市| 山丹县| 大关县| 炉霍县| 海丰县| 全南县| 图们市| 芷江| 鹤庆县| 庄浪县| 宁安市| 图片| 正阳县| 太谷县| 漯河市| 洛南县| 博爱县| 个旧市| 奉贤区| 舞钢市| 蓝山县| 建水县| 华池县| 皮山县| 旬邑县| 鄂托克前旗| 澳门| 汝阳县| 汕头市| 长葛市| 丽水市| 房产|