二叉樹的遍歷

前中后序的遞歸實(shí)現(xiàn)

    void ff(TreeNode* p,vector<int> &v){
        if(p==nullptr)
            return;
        v.push_back(p->val);
        ff(p->left,v);
        ff(p->right,v);
    }
    void mf(TreeNode* p,vector<int> &v){
        if(p==nullptr)
            return;
        mf(p->left,v);
        v.push_back(p->val);
        mf(p->right,v);
    }
    void bf(TreeNode* p,vector<int> &v){
        if(p==nullptr)
            return;
        bf(p->left,v);
        bf(p->right,v);
        v.push_back(p->val);
    }

前中后序的非遞歸標(biāo)準(zhǔn)實(shí)現(xiàn)

    void ff(TreeNode* root,vector<int>& v){
        TreeNode* p=root;
        stack<TreeNode*> s;
        while(!s.empty() || p){
            while(p){
                v.push_back(p->val);//Visit
                s.push(p);
                p=p->left;
            }
            p=s.top();
            s.pop();
            p=p->right;
        }
    }
    void mf(TreeNode* root,vector<int>& v){
        TreeNode* p=root;
        stack<TreeNode*> s;
        while(!s.empty() || p){
            while(p){
                s.push(p);
                p=p->left;
            }
            p=s.top();
            v.push_back(p->val);//Visit
            s.pop();
            p=p->right;
        }
    }
    void bf(TreeNode* root,vector<int>& v){
        TreeNode* p=root,*plast=nullptr;
        stack<TreeNode*> s;
        while(!s.empty() || p){
            while(p){
                s.push(p);
                p=p->left;
            }
            p=s.top();
            if(!p->right || plast==p->right){
                v.push_back(p->val);//Visit
                s.pop();
                plast=p;
                p=nullptr;//既然沒有右子樹或者右子樹已經(jīng)被訪問溶浴,那么p就沒有用了,應(yīng)該讓它失效蔬芥,這樣才會(huì)從棧頂,彈出根節(jié)點(diǎn)
            }else
                p=p->right;
            
        }
    }

總結(jié)

整體的思路是這樣的:

  • 指針p指向root控汉,創(chuàng)建棧
  • 當(dāng)棧不為空或p有效時(shí)笔诵,循環(huán):
    1. 沿著根節(jié)點(diǎn)的左分支一路壓入根節(jié)點(diǎn)(如果是前序遍歷此時(shí)就可以訪問p)
    2. 循環(huán)結(jié)束時(shí)p為空,此時(shí)p失效姑子。
    3. 當(dāng)p失效時(shí)乎婿,從棧頂彈出結(jié)點(diǎn)(如果是中序遍歷此時(shí)就可以訪問p)。此時(shí)彈出的一定是底層的結(jié)點(diǎn)街佑。
    4. 對(duì)該結(jié)點(diǎn)進(jìn)行訪問谢翎,并嘗試訪問其右分支。
    5. 右分支的結(jié)點(diǎn)視作根節(jié)點(diǎn)沐旨,重復(fù)以上過程森逮。

當(dāng)后序遍歷時(shí),預(yù)先設(shè)置plast為空磁携,以記錄上一次訪問的結(jié)點(diǎn)褒侧。
取棧頂元素作p,

  • 當(dāng)p無右分支或p的右分支已被訪問時(shí)
    1. 訪問p
    2. 彈出棧頂
    3. 更新plast
    4. 設(shè)置p失效(p失效之后才會(huì)從棧頂彈元素)
  • 否則(當(dāng)p有右分支且右分支沒有被訪問)
    p=p->right 右分支作根節(jié)點(diǎn)進(jìn)入循環(huán)進(jìn)行訪問

簡單來說谊迄,p相當(dāng)于當(dāng)前操作的子樹闷供;當(dāng)p失效時(shí),彈出棧頂元素鳞上,即使當(dāng)前操作的子樹的根这吻。左邊處理完了,就索引根篙议,通過根來索引右子樹唾糯。

層遍歷與前序遍歷的非標(biāo)準(zhǔn)實(shí)現(xiàn)

前序遍歷的非標(biāo)準(zhǔn)實(shí)現(xiàn)

流程:

  • 判斷root是否為空
  • 指針p指向root
  • 創(chuàng)建棧并壓入p
  • 當(dāng)棧不為空時(shí):循環(huán)
    1. 彈出棧頂元素進(jìn)行訪問
    2. 如果該元素有右分支怠硼,壓入右孩子
    3. 如果該元素有左分支,壓入左孩子
    void ff2(TreeNode* root,vector<int>& v){
        if(!root)
            return;
        TreeNode* p=root;
        stack<TreeNode*> s;
        s.push(p);
        while(!s.empty()){
            p=s.top();
            s.pop();
            v.push_back(p->val);
            if(p->right)
                s.push(p->right);
            if(p->left)
                s.push(p->left);
        }
    }

層遍歷

流程:

  • 判斷root是否為空
  • 指針p指向root
  • 創(chuàng)建隊(duì)列并壓入p
  • 當(dāng)隊(duì)列不為空時(shí):循環(huán)
    1. 彈出隊(duì)首元素進(jìn)行訪問
    2. 如果該元素有左分支移怯,壓入左孩子
    3. 如果該元素有右分支香璃,壓入右孩子
    void layer(TreeNode* root,vector<int> &v){
        if(!root)
            return;
        TreeNode* p=root;
        queue<TreeNode*> q;
        q.push(p);
        
        while(!q.empty()){
            p=q.front();
            v.push_back(p->val);
            q.pop();
            if(p->left)
                q.push(p->left);
            if(p->right)
                q.push(p->right);
        }
    }

擴(kuò)展:層打印

按層打印到一個(gè)二維數(shù)組中。
該問題的難點(diǎn)在于如何判斷當(dāng)前訪問的結(jié)點(diǎn)是不是某一行的行末舟误。所以我們使用nlast指針葡秒,來記錄當(dāng)前行的行末;用last指針來記錄最近壓入隊(duì)列的結(jié)點(diǎn)嵌溢。

  • 初始時(shí)last和nlast都為root
  • 循環(huán)時(shí)眯牧,last記錄最近壓入的結(jié)點(diǎn)
  • 當(dāng)前訪問的結(jié)點(diǎn)等于nlast時(shí),表示已經(jīng)訪問到了某一行的行末赖草。這個(gè)時(shí)候做行末相關(guān)的處理学少,并將nlast更新為last。因?yàn)楫?dāng)某一行訪問完成后秧骑,新壓入的結(jié)點(diǎn)版确,一定是下一行的行尾。
    void layer(TreeNode* root,vector<vector<int> > &v){
        if(!root)
            return;
        TreeNode* p=root;
        queue<TreeNode*> q;
        q.push(p);
        TreeNode* last=root,*nlast=root; //last和nlast初始化為root
        vector<int> temp;
        while(!q.empty()){
            p=q.front();
            q.pop();
            temp.push_back(p->val);
            if(p->left){
                q.push(p->left);
                last=p->left;  //記錄last
            }
            if(p->right){
                q.push(p->right);
                last=p->right;//記錄last
            }
            if(p==nlast){//如果p指針已經(jīng)到達(dá)了一行的行末乎折,那么最新壓入棧的last一定是下一行的行末
                v.push_back(temp);
                temp.clear();
                nlast=last;
            }
        }
    }

擴(kuò)展:判滿绒疗、完全二叉樹

判斷完全二叉樹

思路:按層遍歷二叉樹,直到發(fā)現(xiàn)第一個(gè)非滿結(jié)點(diǎn)(單左或葉子結(jié)點(diǎn)骂澄,不可以是單右)吓蘑。在這個(gè)結(jié)點(diǎn)之后,所有的結(jié)點(diǎn)都是葉子結(jié)點(diǎn)酗洒。那么該樹就是完全二叉樹士修。

    bool chk(TreeNode* root) {
        //按層遍歷,找到第一個(gè)不滿結(jié)點(diǎn)樱衷,其后所有結(jié)點(diǎn)必須是葉子
        if(!root)
            return false;
        TreeNode *p=root;
        queue<TreeNode*> que;
        que.push(p);
        int find=0;
        while(!que.empty()){
            p=que.front();
            que.pop();
            if(p->left){
                que.push(p->left);
            }
            if(p->right){
                que.push(p->right);
            }
            if(!p->right){//沒有右樹棋嘲,就涵蓋了葉子和單左 兩種情況
                find=1;
                continue;
            }else{//如果有右樹,看看是葉子矩桂,還是單右沸移。單右直接返回false
                if(!p->left)
                    return false;
            }
            if(find==1 && (p->left || p->right)){//注意優(yōu)先級(jí),&&高于||
                return false;
            }
        }
        return true;
    }

判斷滿二叉樹

思路:按層遍歷二叉樹侄榴,直至發(fā)現(xiàn)第一個(gè)非滿結(jié)點(diǎn)(且必須是葉子結(jié)點(diǎn))雹锣。在該結(jié)點(diǎn)之后的所有結(jié)點(diǎn)都是葉子結(jié)點(diǎn),且該結(jié)點(diǎn)前一個(gè)節(jié)點(diǎn)是某行的行末癞蚕。
實(shí)現(xiàn):用一個(gè)plast指針來記錄上次訪問的結(jié)點(diǎn)蕊爵。其他按照判完全二叉樹的方法來進(jìn)行即可。代碼未經(jīng)過驗(yàn)證就不寫了桦山。另外攒射,nlast更新時(shí)可以按照nlast=nlast->right來更新醋旦,這樣就不用last指針了。

擴(kuò)展:二叉樹的規(guī)則化構(gòu)建

根節(jié)點(diǎn)的值是1会放,其左右孩子分別是1和2饲齐,每個(gè)孩子的左右孩子依然是1和2。以此來構(gòu)建N層的二叉樹咧最。
思路:首先需要保證N大于0.建立總根節(jié)點(diǎn)root捂人,然后初始化p和nlast為root。然后共遍歷n-2層矢沿,每一層遍歷時(shí)都把結(jié)點(diǎn)添加左右孩子滥搭。比如n==3時(shí),應(yīng)該遍歷中間的一層就好捣鲸。

    TreeNode* buildBt(int n){
        if(!n)
            return nullptr;
        TreeNode* root=new TreeNode(1);
        TreeNode *p=root,*nlast=root;
        queue<TreeNode*> que;
        que.push(p);
        ----n;
        while(n){
            p=que.front();
            que.pop();
            p->left=new TreeNode(1);
            p->right=new TreeNode(2);
            que.push(p->left);
            que.push(p->right);
            if(p==nlast->right){//如果發(fā)現(xiàn)遍歷至某一行尾论熙,則更新n和nlast
                --n;
                nlast=p;
                if(n<=0)
                    break;
            }
        }
        return root;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市摄狱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌无午,老刑警劉巖媒役,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異宪迟,居然都是意外死亡酣衷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門次泽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來穿仪,“玉大人,你說我怎么就攤上這事意荤“∑” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵玖像,是天一觀的道長紫谷。 經(jīng)常有香客問我,道長捐寥,這世上最難降的妖魔是什么笤昨? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮握恳,結(jié)果婚禮上瞒窒,老公的妹妹穿的比我還像新娘。我一直安慰自己乡洼,他們只是感情好崇裁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布匕坯。 她就那樣靜靜地躺著,像睡著了一般寇壳。 火紅的嫁衣襯著肌膚如雪醒颖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天壳炎,我揣著相機(jī)與錄音泞歉,去河邊找鬼。 笑死匿辩,一個(gè)胖子當(dāng)著我的面吹牛腰耙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铲球,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼挺庞,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了稼病?” 一聲冷哼從身側(cè)響起选侨,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎然走,沒想到半個(gè)月后援制,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芍瑞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年晨仑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拆檬。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡洪己,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出竟贯,到底是詐尸還是另有隱情答捕,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布屑那,位于F島的核電站噪珊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏齐莲。R本人自食惡果不足惜痢站,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望选酗。 院中可真熱鬧阵难,春花似錦、人聲如沸芒填。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至朱庆,卻和暖如春盛泡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背娱颊。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工傲诵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人箱硕。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓拴竹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親剧罩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子栓拜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • -先序遍歷: 訪問根結(jié)點(diǎn),先序遍歷其左子樹惠昔,先序遍歷其右子樹幕与;運(yùn)用到遞歸void PreOrderTraversa...
    Spicy_Crayfish閱讀 2,028評(píng)論 0 0
  • 二叉樹的三種常用遍歷方式 學(xué)習(xí)過數(shù)據(jù)結(jié)構(gòu)的同學(xué)都清楚,除了層序遍歷外镇防,二叉樹主要有三種遍歷方式: 1. 先序遍歷...
    SherlockBlaze閱讀 1,230評(píng)論 0 4
  • 樹(tree)是一種抽象數(shù)據(jù)類型(ADT)或是實(shí)作這種抽象數(shù)據(jù)類型的數(shù)據(jù)結(jié)構(gòu)纽门,用來模擬具有樹狀結(jié)構(gòu)性質(zhì)的數(shù)據(jù)集合。...
    曾大穩(wěn)丶閱讀 1,001評(píng)論 0 1
  • 數(shù)據(jù)結(jié)構(gòu)和算法--二叉樹的實(shí)現(xiàn) 幾種二叉樹 1营罢、二叉樹 和普通的樹相比,二叉樹有如下特點(diǎn): 每個(gè)結(jié)點(diǎn)最多只有兩棵子...
    sunhaiyu閱讀 6,461評(píng)論 0 14
  • 現(xiàn)在決定把自己很久以前的一些文章重新markdown一下饼齿,發(fā)到簡書來饲漾,先從這篇二叉樹的遍歷說起的。大家都知道二叉樹...
    董成鵬閱讀 365評(píng)論 0 1