0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

二叉樹的前序遍歷非遞歸實(shí)現(xiàn)

算法與數(shù)據(jù)結(jié)構(gòu) ? 來源:袁廚的算法小屋 ? 作者:袁廚的算法小屋 ? 2021-05-28 13:59 ? 次閱讀

我們之前說了二叉樹基礎(chǔ)及二叉的幾種遍歷方式及練習(xí)題,今天我們來看一下二叉樹的前序遍歷非遞歸實(shí)現(xiàn)。

前序遍歷的順序是, 對(duì)于樹中的某節(jié)點(diǎn),先遍歷該節(jié)點(diǎn),然后再遍歷其左子樹,最后遍歷其右子樹。

我們先來通過下面這個(gè)動(dòng)畫復(fù)習(xí)一下二叉樹的前序遍歷。

迭代遍歷

我們?cè)囅胍幌?,之前我們借助?duì)列幫我們實(shí)現(xiàn)二叉樹的層序遍歷,

那么可不可以,也借助數(shù)據(jù)結(jié)構(gòu),幫助我們實(shí)現(xiàn)二叉樹的前序遍歷。

假設(shè)我們的二叉樹為 [1,2,3]。我們需要對(duì)其進(jìn)行前序遍歷。其遍歷順序?yàn)?/p>

當(dāng)前節(jié)點(diǎn) 1,左孩子 2,右孩子 3。

這里可不可以用棧,幫我們完成前序遍歷呢?

棧和隊(duì)列的那些事

我們都知道棧的特性是先進(jìn)后出,我們借助棧來幫助我們完成前序遍歷的時(shí)候。

則需要注意的一點(diǎn)是,我們應(yīng)該先將右子節(jié)點(diǎn)入棧,再將左子節(jié)點(diǎn)入棧。

這樣出棧時(shí),則會(huì)先出左節(jié)點(diǎn),再出右子節(jié)點(diǎn),則能夠完成樹的前序遍歷。

我們用一句話對(duì)上圖進(jìn)行總結(jié),當(dāng)棧不為空時(shí),棧頂元素出棧,如果其右孩子不為空,則右孩子入棧,其左孩子不為空,則左孩子入棧。還有一點(diǎn)需要注意的是,我們和層序遍歷一樣,需要先將 root 節(jié)點(diǎn)進(jìn)行入棧,然后再執(zhí)行 while 循環(huán)。

看到這里你已經(jīng)能夠自己編寫出代碼了,不信你去試試。

時(shí)間復(fù)雜度:O(n) 需要對(duì)所有節(jié)點(diǎn)遍歷一次

空間復(fù)雜度:O(n) 棧的開銷,平均為 O(logn) 最快情況,即斜二叉樹時(shí)為 O(n)

參考代碼

class Solution {

public List《Integer》 preorderTraversal(TreeNode root) {

List《Integer》 list = new ArrayList《》();

Stack《TreeNode》 stack = new Stack《》();

if (root == null) return list;

stack.push(root);

while (!stack.isEmpty()) {

TreeNode temp = stack.pop();

if (temp.right != null) {

stack.push(temp.right);

}

if (temp.left != null) {

stack.push(temp.left);

}

//這里也可以放到前面

list.add(temp.val);

}

return list;

}

}

Morris

Morris 遍歷利用樹的左右孩子為空(大量空閑指針),實(shí)現(xiàn)空間開銷的極限縮減。這個(gè)遍歷方式,稍微有那么一丟丟難理解,不過結(jié)合動(dòng)圖,也就一目了然,下面我們先看動(dòng)畫吧。

看完視頻,是不是感覺自己搞懂了,又感覺自己沒搞懂,哈哈,咱們繼續(xù)往下看。

我們之前說的,Morris 遍歷利用了樹中大量空閑指針的特性,我們需要找到當(dāng)前節(jié)點(diǎn)的左子樹中的最右邊的葉子節(jié)點(diǎn),將該葉子節(jié)點(diǎn)的 right 指向當(dāng)前節(jié)點(diǎn)。例如當(dāng)前節(jié)點(diǎn)為2,其左子樹中的最右節(jié)點(diǎn)為 9 ,則在 9 節(jié)點(diǎn)添加一個(gè) right 指針指向 2。

其實(shí)上圖中的 Morris 遍歷遵循兩個(gè)原則,我們?cè)趧?dòng)畫中也能夠得出。

1.當(dāng) p1.left == null 時(shí),p1 = p1.right。(這也就是我們?yōu)槭裁匆o葉子節(jié)點(diǎn)添加 right 指針的原因)

2.如果 p1.left != null,找到 p1 左子樹上最右的節(jié)點(diǎn)。(也就是我們的 p2 最后停留的位置),此時(shí)我們又可以分為兩種情況,一種是葉子節(jié)點(diǎn)添加 right 指針的情況,一種是去除葉子節(jié)點(diǎn) right 指針的情況。

如果 p2 的 right 指針指向空,讓其指向 p1,p1向左移動(dòng),即 p1 = p1.left

如果 p2 的 right 指針指向 p1,讓其指向空,(為了防止重復(fù)執(zhí)行,則需要去掉 right 指針)p1 向右移動(dòng),p1 = p1.right。

這時(shí)你可以結(jié)合咱們剛才提到的兩個(gè)原則,再去看一遍動(dòng)畫,并代入規(guī)則進(jìn)行模擬,差不多就能完全搞懂啦。

下面我們來對(duì)動(dòng)畫中的內(nèi)容進(jìn)行拆解 ,

首先 p1 指向 root節(jié)點(diǎn)

p2 = p1.left,下面我們需要通過 p2 找到 p1的左子樹中的最右節(jié)點(diǎn)。即節(jié)點(diǎn) 5,然后將該節(jié)點(diǎn)的 right 指針指向 root。并記錄 root 節(jié)點(diǎn)的值。

向左移動(dòng) p1,即 p1 = p1.left

p2 = p1.left ,即節(jié)點(diǎn) 4 ,找到 p1 的左子樹中的最右葉子節(jié)點(diǎn),也就是 9,并將該節(jié)點(diǎn)的 right 指針指向 2。

繼續(xù)向左移動(dòng) p1,即 p1 = p1.left,p2 = p1.left。也就是節(jié)點(diǎn) 8。并將該節(jié)點(diǎn)的 right 指針指向 p1。

我們發(fā)現(xiàn)這一步給前兩步是一樣的,都是找到葉子節(jié)點(diǎn),將其 right 指針指向 p1,此時(shí)我們完成了添加 right 指針的過程,下面我們繼續(xù)往下看。

我們繼續(xù)移動(dòng) p1 指針,p1 = p1.left。p2 = p.left。此時(shí)我們發(fā)現(xiàn) p2 == null,即下圖此時(shí)我們需要移動(dòng) p1, 但是不再是 p1 = p1.left 而是 p1 = p1.right。也就是 4,繼續(xù)讓 p2 = p1.left。此時(shí)則為下圖這種情況

此時(shí)我們發(fā)現(xiàn) p2.right != null 而是指向 4,說明此時(shí)我們已經(jīng)添加過了 right 指針,所以去掉 right 指針,并讓 p1 = p1.right

下面則繼續(xù)移動(dòng) p1 ,按照規(guī)則繼續(xù)移動(dòng)即可,遇到的情況已經(jīng)在上面做出了舉例,所以下面我們就不繼續(xù)贅述啦,如果還不是特別理解的同學(xué),可以再去看一遍動(dòng)畫加深下印象。時(shí)間復(fù)雜度 O(n),空間復(fù)雜度 O(1)下面我們來看代碼吧。

代碼

class Solution {

public List《Integer》 preorderTraversal(TreeNode root) {

List《Integer》 list = new ArrayList《》();

if (root == null) {

return list;

}

TreeNode p1 = root; TreeNode p2 = null;

while (p1 != null) {

p2 = p1.left;

if (p2 != null) {

//找到左子樹的最右葉子節(jié)點(diǎn)

while (p2.right != null && p2.right != p1) {

p2 = p2.right;

}

//添加 right 指針,對(duì)應(yīng) right 指針為 null 的情況

if (p2.right == null) {

list.add(p1.val);

p2.right = p1;

p1 = p1.left;

continue;

}

//對(duì)應(yīng) right 指針存在的情況,則去掉 right 指針

p2.right = null;

} else {

list.add(p1.val);

}

//移動(dòng) p1

p1 = p1.right;

}

return list;

}

}

原文標(biāo)題:把二叉樹揉碎(二)

文章出處:【微信公眾號(hào):算法與數(shù)據(jù)結(jié)構(gòu)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

責(zé)任編輯:haq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 二叉樹
    +關(guān)注

    關(guān)注

    0

    文章

    74

    瀏覽量

    12302

原文標(biāo)題:把二叉樹揉碎(二)

文章出處:【微信號(hào):TheAlgorithm,微信公眾號(hào):算法與數(shù)據(jù)結(jié)構(gòu)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    什么是默克爾(Merkle Tree)?如何計(jì)算默克爾根?

    01 默克爾的概念 默克爾(Merkle Tree)是一種特殊的二叉樹,它的每個(gè)節(jié)點(diǎn)都存儲(chǔ)了一個(gè)數(shù)據(jù)塊的哈希值。哈希值是一種可以將任意長(zhǎng)度的數(shù)據(jù)轉(zhuǎn)換為固定長(zhǎng)度的字符串的算法,它具有唯一性和不可
    的頭像 發(fā)表于 09-30 18:22 ?479次閱讀
    什么是默克爾<b class='flag-5'>樹</b>(Merkle Tree)?如何計(jì)算默克爾根?

    Python遞歸的經(jīng)典案例

    當(dāng)我們碰到諸如需要求階乘或斐波那契數(shù)列的問題時(shí),使用普通的循環(huán)往往比較麻煩,但如果我們使用遞歸時(shí),會(huì)簡(jiǎn)單許多,起到事半功倍的效果。這篇文章主要和大家分享一些和遞歸有關(guān)的經(jīng)典案例,結(jié)合一些資料談一下個(gè)人的理解,也借此加深自己對(duì)遞歸
    的頭像 發(fā)表于 08-05 15:57 ?262次閱讀

    遞歸神經(jīng)網(wǎng)絡(luò)的實(shí)現(xiàn)方法

    (Recurrent Neural Network,通常也簡(jiǎn)稱為RNN,但在此處為區(qū)分,我們將循環(huán)神經(jīng)網(wǎng)絡(luò)稱為Recurrent RNN)不同,遞歸神經(jīng)網(wǎng)絡(luò)更側(cè)重于處理樹狀或圖結(jié)構(gòu)的數(shù)據(jù),如句法分析、自然語言的語法結(jié)構(gòu)等。以下將從遞歸
    的頭像 發(fā)表于 07-10 17:02 ?261次閱讀

    指電極上覆蓋敏感材料的阻值計(jì)算

    覆蓋的敏感材料厚度超出指厚度時(shí)計(jì)算電阻,是否可以視作指電極指間電阻多個(gè)周期串聯(lián)后與超出指厚度部分敏感材料電阻并聯(lián)
    發(fā)表于 07-05 14:48

    遞歸神經(jīng)網(wǎng)絡(luò)主要應(yīng)用于哪種類型數(shù)據(jù)

    處理(NLP) 自然語言處理是遞歸神經(jīng)網(wǎng)絡(luò)最重要的應(yīng)用領(lǐng)域之一。在NLP中,遞歸神經(jīng)網(wǎng)絡(luò)可以用于以下任務(wù): 1.1 語言模型(Language Modeling) 語言模型是預(yù)測(cè)給定詞序列中下一個(gè)詞的概率分布。遞歸神經(jīng)網(wǎng)絡(luò)可以捕
    的頭像 發(fā)表于 07-04 14:58 ?492次閱讀

    遞歸神經(jīng)網(wǎng)絡(luò)是循環(huán)神經(jīng)網(wǎng)絡(luò)嗎

    遞歸神經(jīng)網(wǎng)絡(luò)(Recurrent Neural Network,簡(jiǎn)稱RNN)和循環(huán)神經(jīng)網(wǎng)絡(luò)(Recurrent Neural Network,簡(jiǎn)稱RNN)實(shí)際上是同一個(gè)概念,只是不同的翻譯方式
    的頭像 發(fā)表于 07-04 14:54 ?596次閱讀

    關(guān)于C語言中的遞歸

    遞歸指的是在函數(shù)的定義中使用函數(shù)自身的方法。
    發(fā)表于 02-26 10:34 ?312次閱讀
    關(guān)于C語言中的<b class='flag-5'>遞歸</b>

    榮小菜補(bǔ)鈣記第61期_LabVIEW之遞歸文件及文件夾

    深入層級(jí)遍歷,直到不再出現(xiàn)文件夾。While循環(huán)最終運(yùn)行次數(shù)反映了最大層級(jí)。 4. 遍歷全部層級(jí)文件及文件夾_遞歸文件列表函數(shù) 除了自己寫代碼實(shí)現(xiàn)該功能,LabVIEW也提供了“
    發(fā)表于 02-16 21:36

    哈夫曼編碼怎么算 哈夫曼編碼左邊是0還是1

    二叉樹,將出現(xiàn)頻率高的字符用較短的編碼表示,而出現(xiàn)頻率低的字符則用較長(zhǎng)的編碼表示。通過這種方式,可以實(shí)現(xiàn)對(duì)數(shù)據(jù)進(jìn)行高效的編碼和解碼。 下面我們將詳細(xì)介紹哈夫曼編碼的算法過程。 統(tǒng)計(jì)字符頻率 在進(jìn)行哈夫曼編碼前,首先需
    的頭像 發(fā)表于 01-30 11:27 ?2431次閱讀

    一種面向標(biāo)識(shí)公共遞歸解析節(jié)點(diǎn)的數(shù)據(jù)安全加固策略

    摘要 :為解決工業(yè)互聯(lián)網(wǎng)標(biāo)識(shí)解析體系公共遞歸解析節(jié)點(diǎn)信息透明、缺乏隱私數(shù)據(jù)保護(hù)和身份權(quán)限管理等問題,提出了一種面向標(biāo)識(shí)公共遞歸解析節(jié)點(diǎn)的數(shù)據(jù)安全加固策略。通過設(shè)計(jì)加密機(jī)制及細(xì)粒度權(quán)限查驗(yàn)機(jī)制,實(shí)現(xiàn)了標(biāo)識(shí)解析
    的頭像 發(fā)表于 12-26 11:27 ?612次閱讀
    一種面向標(biāo)識(shí)公共<b class='flag-5'>遞歸</b>解析節(jié)點(diǎn)的數(shù)據(jù)安全加固策略

    堆的實(shí)現(xiàn)思路

    什么是堆? 堆是一種 基于樹結(jié)構(gòu)的數(shù)據(jù)結(jié)構(gòu),它是一棵二叉樹 ,具有以下兩個(gè)特點(diǎn): 堆是一個(gè)完全二叉樹,即除了最后一層,其他層都是滿的,最后一層從左到右填滿。 堆中每個(gè)節(jié)點(diǎn)都滿足堆的特性,即父節(jié)點(diǎn)的值
    的頭像 發(fā)表于 11-24 16:02 ?378次閱讀
    堆的<b class='flag-5'>實(shí)現(xiàn)</b>思路

    二叉樹的定義

    型結(jié)構(gòu) 是一類重要的 非線性數(shù)據(jù)結(jié)構(gòu) ,其中以二叉樹最為常用,直觀來看,是以分支關(guān)系定義的層次結(jié)構(gòu)。型結(jié)構(gòu)在客觀世界中廣泛存在,比
    的頭像 發(fā)表于 11-24 15:57 ?1246次閱讀
    <b class='flag-5'>樹</b>與<b class='flag-5'>二叉樹</b>的定義

    python如何遍歷列表并提取

    遍歷列表是Python中非常常見的操作之一,可以使用for循環(huán)或者while循環(huán)來實(shí)現(xiàn)。下面我將詳細(xì)介紹如何使用for循環(huán)遍歷列表并提取元素。 首先,讓我們簡(jiǎn)單了解一下Python中的列表。列表
    的頭像 發(fā)表于 11-23 15:55 ?1242次閱讀

    OP-TEE安全存儲(chǔ)安全文件的格式

    時(shí),默認(rèn)情況下, 加密后的數(shù)據(jù)會(huì)被保存在/data/tee目錄中。 安全存儲(chǔ)功能使用 二叉樹的方式來 保存加密后的文件。 當(dāng)?shù)谝淮问褂冒踩鎯?chǔ)功能創(chuàng)建用于保存敏感數(shù)據(jù)的安全文件時(shí),OP-TEE將會(huì)在/data/tee目錄中生成兩個(gè)文件:dirf.db文件和以數(shù)字命名的文件。 dirf.db文
    的頭像 發(fā)表于 11-21 11:49 ?692次閱讀
    OP-TEE安全存儲(chǔ)安全文件的格式

    什么情況下需要布隆過濾器

    , gmail等郵箱垃圾郵件過濾功能 這幾個(gè)例子有一個(gè)共同的特點(diǎn):如何判斷一個(gè)元素是否存在一個(gè)集合中? 常規(guī)思路 數(shù)組 鏈表 、平衡二叉樹、Trie Map (紅黑) 哈希表 雖然上面描述的這幾種數(shù)據(jù)結(jié)構(gòu)配合常見的排序、
    的頭像 發(fā)表于 11-11 11:37 ?623次閱讀
    什么情況下需要布隆過濾器