Object-c 二叉樹的遍歷(前序贰您、中序坏平、后序以及非遞歸遍歷)

  • 二叉樹的結(jié)構(gòu)
    二叉樹是樹的特殊形式,它包含結(jié)點(diǎn)值(可空)锦亦,左孩子結(jié)點(diǎn)(可空)舶替,右孩子結(jié)點(diǎn)(可空)「茉埃空樹即三者均為空顾瞪,當(dāng)任一結(jié)點(diǎn)只有左孩子或右孩子時(shí),這顆樹的結(jié)構(gòu)就與鏈表類似了抛蚁。
  • 定義一個(gè)二叉樹的結(jié)點(diǎn)代碼清單如下:
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface TreeNode : NSObject

@property(nonatomic,assign) NSInteger value;
@property(nonatomic,strong,nullable) TreeNode* leftChild;
@property(nonatomic,strong,nullable) TreeNode* rightChild;

-(instancetype) initWithData:(NSInteger) data;


@end

NS_ASSUME_NONNULL_END

  • 創(chuàng)建一顆二叉樹代碼清單如下:
-(TreeNode*) createTree:(NSArray *)data{
    TreeNode* p1 = [[TreeNode alloc] initWithData:1];
    TreeNode* p2 = [[TreeNode alloc] initWithData:2];
    TreeNode* p3 = [[TreeNode alloc] initWithData:3];
    TreeNode* p4 = [[TreeNode alloc] initWithData:4];
    TreeNode* p5 = [[TreeNode alloc] initWithData:5];
    TreeNode* p6 = [[TreeNode alloc] initWithData:6];
    TreeNode* p7 = [[TreeNode alloc] initWithData:7];
    TreeNode* p8 = [[TreeNode alloc] initWithData:8];
    TreeNode* p9 = [[TreeNode alloc] initWithData:9];
    p1.leftChild = p2;
    p1.rightChild = p3;
    p2.leftChild = p4;
    p2.rightChild = p5;
    p3.leftChild = p6;
    p3.rightChild = p7;
    p4.leftChild = p8;
    p4.rightChild = p9;
    self.root = p1;
    return p1;
}

對(duì)應(yīng)的樹抽象結(jié)構(gòu)為:


Untitled Diagram-5.png
  • 二叉樹的前序遍歷(遞歸)規(guī)則為:根陈醒,左,右

1.先訪問根結(jié)點(diǎn)瞧甩,如果有左孩子結(jié)點(diǎn)再訪問左孩子結(jié)點(diǎn)钉跷,如果有右孩子結(jié)點(diǎn)再訪問右孩子結(jié)點(diǎn)。
2.然后再把左孩子結(jié)點(diǎn)當(dāng)作根結(jié)點(diǎn)肚逸,重復(fù)一次步驟1爷辙。
3.再把右孩子結(jié)點(diǎn)當(dāng)作根結(jié)點(diǎn),重復(fù)一次步驟1朦促。
4.直到當(dāng)前結(jié)點(diǎn)為葉子結(jié)點(diǎn)為止膝晾。
前序遍歷的結(jié)果為:1,2务冕,4血当,8,9洒疚,5歹颓,3,6油湖,7

*前序遍歷(遞歸方式)代碼為:

-(void) preOrder:(TreeNode *)root{
    if (!root) {
        return;
    }
    [self visitedNode:root];
    if(root.leftChild){
        [self preOrder:root.leftChild];
    }
    if(root.rightChild){
        [self preOrder:root.rightChild];
    }
}
  • 前序遍歷(非遞歸借助棧實(shí)現(xiàn))具體實(shí)現(xiàn)邏輯如下:

1.根結(jié)點(diǎn)入棧
2.while棧不為空時(shí)巍扛,取棧頂元素(假設(shè)為e)并訪問。判斷e是否有左孩子結(jié)點(diǎn)乏德,如果有則入棧撤奸,判斷e是否有右孩子結(jié)點(diǎn)吠昭,如果有則入棧。

  • 前序遍歷(非遞歸借助棧實(shí)現(xiàn))代碼清單為:
/// 非遞歸實(shí)現(xiàn)前序遍歷
/// @param root 根結(jié)點(diǎn)
-(void) iterativePreOrder:(TreeNode*) root{
    
    if (!self.stack) {
        self.stack = [[Stack alloc] initWithSize:100];
    }
    TreeNode* p = root;
    if(p){
        printf(" \n前序非遞歸遍歷的結(jié)果為:\n ");
        [self.stack push:p];
        while (![self.stack isEmpty]) {
            p = (TreeNode*)[self.stack pop];
            [self visitedNode:p];
            
            if(p.rightChild){
                [self.stack push:p.rightChild];
            }
            if(p.leftChild){
                [self.stack push:p.leftChild];
            }
            
        }
    }
}
  • 二叉樹的中序遍歷(遞歸)規(guī)則為:左胧瓜,根矢棚,右

1.對(duì)樹中任一結(jié)點(diǎn)判斷當(dāng)前結(jié)點(diǎn)是否有左孩子,如果有左孩子府喳,繼續(xù)尋找蒲肋,直到當(dāng)前結(jié)點(diǎn)沒有左孩子為止才訪問。
2.訪問當(dāng)前結(jié)點(diǎn)的父結(jié)點(diǎn)钝满。
3.訪問當(dāng)前結(jié)點(diǎn)的右孩子結(jié)點(diǎn)兜粘。
4.直到當(dāng)前結(jié)點(diǎn)為葉子結(jié)點(diǎn)為止。
中序遍歷的結(jié)果為:8弯蚜,4孔轴,9,5碎捺,2路鹰,1,6收厨,3晋柱,7

  • 二叉樹的中序遍歷(遞歸)代碼為:
-(void) inOrder:(TreeNode *)root{
    if (!root) {
        return;
    }
    if(root.leftChild){
        [self inOrder:root.leftChild];
    }
    [self visitedNode:root];
    if(root.rightChild){
        [self inOrder:root.rightChild];
    }
}
  • 二叉樹的中序遍歷(非遞歸)邏輯如下:

對(duì)于任一結(jié)點(diǎn)P,
1.若其左孩子不為空帽氓,則將P入棧并將P的左孩子置為當(dāng)前的P趣斤,然后對(duì)當(dāng)前結(jié)點(diǎn)P再進(jìn)行相同的處理俩块;
2.若其左孩子為空黎休,則取棧頂元素并進(jìn)行出棧操作,訪問該棧頂結(jié)點(diǎn)玉凯,然后將當(dāng)前的P置為棧頂結(jié)點(diǎn)的右孩子势腮;
3.直到P為nil或者棧為空則遍歷結(jié)束。

Untitled Diagram-6.png
  • 二叉樹的中序遍歷(非遞歸)代碼清單如下:
/// 非遞歸實(shí)現(xiàn)中序遍歷
/// @param root 根結(jié)點(diǎn)
-(void) iterativeInOrder:(TreeNode*) root{
    if (!self.stack) {
        self.stack = [[Stack alloc] initWithSize:100];
    }
    printf(" \n中序非遞歸遍歷的結(jié)果為:\n ");
    TreeNode* p = root;
    while (p || ![self.stack isEmpty]) {
        
        while (p) {
            [self.stack push:p];
            p = p.leftChild;
        }
        if (![self.stack isEmpty]) {
            p = (TreeNode*)[self.stack pop];
            [self visitedNode:p];
            p = p.rightChild;
        }
    }
}
  • 二叉樹的后序遍歷(遞歸)規(guī)則為:左漫仆,右捎拯,根

1.對(duì)樹中任一結(jié)點(diǎn)判斷當(dāng)前結(jié)點(diǎn)是否有左孩子,如果有左孩子盲厌,繼續(xù)尋找署照,直到當(dāng)前結(jié)點(diǎn)沒有左孩子為止才訪問。
2.訪問當(dāng)前結(jié)點(diǎn)的右孩子結(jié)點(diǎn)吗浩。
3.訪問當(dāng)前結(jié)點(diǎn)的父親結(jié)點(diǎn)建芙。
4.直到當(dāng)前結(jié)點(diǎn)為葉子結(jié)點(diǎn)為止。
后序遍歷的結(jié)果為:8懂扼,9禁荸,4右蒲,5,2赶熟,6瑰妄,7,3映砖,1

  • 二叉樹的后序遍歷(遞歸)代碼清單:
-(void) postOrder:(TreeNode *)root{
    if (!root) {
        return;
    }
    if(root.leftChild){
        [self postOrder:root.leftChild];
    }
    if(root.rightChild){
        [self postOrder:root.rightChild];
    }
    [self visitedNode:root];
}
  • 二叉樹的后序遍歷(非遞歸)邏輯:

1.要保證根結(jié)點(diǎn)在左孩子和右孩子訪問之后才能訪問间坐,因此對(duì)于任一結(jié)點(diǎn)P,先將其入棧邑退。如果P不存在左孩子和右孩子眶诈,則可以直接訪問它;
2.或者P存在左孩子或者右孩子瓜饥,但是其左孩子和右孩子都已被訪問過了逝撬,則同樣可以直接訪問該結(jié)點(diǎn)。若非上述兩種情況乓土,則將P的右孩子和左孩子依次入棧宪潮,這樣就保證了每次取棧頂元素的時(shí)候,左孩子在右孩子前面被訪問趣苏,左孩子和右孩子都在根結(jié)點(diǎn)前面被訪問狡相。

  • 二叉樹的后序遍歷(非遞歸)代碼清單:
/// 非遞歸實(shí)現(xiàn)后序遍歷
/// @param root 根結(jié)點(diǎn)
-(void) iterativePostOrder:(TreeNode*) root{
    if (!self.stack) {
           self.stack = [[Stack alloc] initWithSize:100];
     }
    [self.stack push:root];
    TreeNode* cur;
    TreeNode* pre;
    printf(" \n后序非遞歸遍歷的結(jié)果為:\n ");
    while(![self.stack isEmpty]){
        cur = (TreeNode*)[self.stack top];
        
        //沒有孩子結(jié)點(diǎn)
        BOOL noChild = !cur.leftChild && !cur.rightChild;
        //通過判斷上一個(gè)結(jié)點(diǎn)是否為其中的一個(gè)孩子結(jié)點(diǎn)時(shí),來判斷孩子結(jié)點(diǎn)是否都已經(jīng)被訪問過
        //如果當(dāng)前結(jié)點(diǎn)存在左右孩子那么必須等它左右孩子都必需訪問之后才能訪問它
        BOOL lastVisitedNode = pre && ([pre isEqual:cur.leftChild] || [pre isEqual: cur.rightChild]);
        if (noChild || lastVisitedNode) {
            [self visitedNode:cur];
            [self.stack pop];
            pre = cur;
        }else{
            if(cur.rightChild){
                [self.stack push:cur.rightChild];
            }
            if(cur.leftChild){
                [self.stack push:cur.leftChild];
            }
        }
    }
}

-(void) visitedNode:(TreeNode*)node{
    if (node) {
        printf(" %ld ",node.value);
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末食磕,一起剝皮案震驚了整個(gè)濱河市尽棕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌彬伦,老刑警劉巖滔悉,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異单绑,居然都是意外死亡回官,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門搂橙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來歉提,“玉大人,你說我怎么就攤上這事区转√蓿” “怎么了?”我有些...
    開封第一講書人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵废离,是天一觀的道長(zhǎng)侄泽。 經(jīng)常有香客問我,道長(zhǎng)厅缺,這世上最難降的妖魔是什么蔬顾? 我笑而不...
    開封第一講書人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任宴偿,我火速辦了婚禮,結(jié)果婚禮上诀豁,老公的妹妹穿的比我還像新娘窄刘。我一直安慰自己,他們只是感情好舷胜,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開白布娩践。 她就那樣靜靜地躺著,像睡著了一般烹骨。 火紅的嫁衣襯著肌膚如雪翻伺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,549評(píng)論 1 312
  • 那天沮焕,我揣著相機(jī)與錄音吨岭,去河邊找鬼。 笑死峦树,一個(gè)胖子當(dāng)著我的面吹牛辣辫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播魁巩,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼急灭,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了谷遂?” 一聲冷哼從身側(cè)響起葬馋,我...
    開封第一講書人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肾扰,沒想到半個(gè)月后畴嘶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡白对,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年掠廓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甩恼。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖沉颂,靈堂內(nèi)的尸體忽然破棺而出条摸,到底是詐尸還是另有隱情,我是刑警寧澤铸屉,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布钉蒲,位于F島的核電站,受9級(jí)特大地震影響彻坛,放射性物質(zhì)發(fā)生泄漏顷啼。R本人自食惡果不足惜踏枣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望钙蒙。 院中可真熱鬧茵瀑,春花似錦、人聲如沸躬厌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)扛施。三九已至鸿捧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疙渣,已是汗流浹背匙奴。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妄荔,地道東北人饥脑。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像懦冰,于是被迫代替她去往敵國(guó)和親灶轰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361