今天是二叉樹層序遍歷專項(xiàng)訓(xùn)練月腋!

今天全部是層序遍歷蟀架!


  1. 102.二叉樹的層序遍歷
    我愿稱之為最初的經(jīng)典。兩天前的文章已經(jīng)詳細(xì)分析過榆骚,在此就不再分析片拍。在此處粘貼出本題的代碼意在展示層序遍歷的基本結(jié)構(gòu),后面的題目都是在此結(jié)構(gòu)的基礎(chǔ)上進(jìn)行修改妓肢,因此可對(duì)照查看從而牢記此結(jié)構(gòu)捌省。
class Solution {
    public List<List<Integer>> resultList = new ArrayList<>();

    public List<List<Integer>> levelOrder(TreeNode root) {
        checkFun02(root);
        return resultList;
    }

    public void checkFun02(TreeNode root){
        if(root == null) return;
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);

        while(!queue.isEmpty()){
            List<Integer> list = new ArrayList<>();
            int levelSize = queue.size();

            while(levelSize > 0){
                TreeNode tempNode = queue.poll();
                list.add(tempNode.val);

                if(tempNode.left != null) queue.offer(tempNode.left);
                if(tempNode.right != null) queue.offer(tempNode.right);
                levelSize--;
            }
            resultList.add(list);
        }
    }
}

這是基礎(chǔ)代碼框架,后續(xù)題目的代碼都是在此基礎(chǔ)上進(jìn)行修改得到的碉钠,我會(huì)在后續(xù)代碼中以注釋的方式來注明 后續(xù)題目中 根據(jù)題目的要求 需要在此框架的基礎(chǔ)上作出修改的地方纲缓。


  1. 107.二叉樹的層次遍歷II
    本題是在層序遍歷的基礎(chǔ)上,要求輸出的順序是自底向上喊废。那其實(shí)就是在基礎(chǔ)的層序遍歷基礎(chǔ)上祝高,每層遍歷完之后的結(jié)果加入到List鏈表的頭節(jié)點(diǎn)處。完整代碼如下:
class Solution { //Java
    public List<List<Integer>> result = new LinkedList<>();

    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        checkFun03(root);
        return result;
    }

    public void checkFun03(TreeNode root){
        if(root == null) return;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);

        while(!queue.isEmpty()){
            int levelSize = queue.size();
            List<Integer> curLevel = new ArrayList<>();

            for(int i = 0; i < levelSize; i++){
                TreeNode curNode = queue.poll();
                curLevel.add(curNode.val);

                if(curNode.left != null){
                    queue.add(curNode.left);
                }
                if(curNode.right != null){
                    queue.add(curNode.right);
                }
            }
            result.add(0,curLevel); // Add the node of the current layer to the head of the linked list
        }
        

    }
}

  1. 199.二叉樹的右視圖
    本題是要輸出每一層的最右側(cè)的元素污筷。那就是在每層遍歷到最后一個(gè)元素時(shí)工闺,把該元素的值加入到結(jié)果集result中就好。
class Solution { // Java
    List<Integer> result = new ArrayList<>();
    public List<Integer> rightSideView(TreeNode root) {
        findMostRightList(root);
        return result;
    }

    public void findMostRightList(TreeNode root){
        if(root == null) return;
        
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);

        while(!queue.isEmpty()){
            int levelSize = queue.size();
            TreeNode rightMostNode = null;

            while(levelSize > 0){
                TreeNode currentNode = queue.poll();
                rightMostNode = currentNode; // to locate the most right Node

                if(currentNode.left != null) queue.add(currentNode.left);
                if(currentNode.right != null) queue.add(currentNode.right);
                levelSize--;
            }

            if(rightMostNode != null){
                result.add(rightMostNode.val); // add the value to result
            }
        }
    }
}

可以看到瓣蛀,我們是定義了一個(gè)叫做rightMostNode的變量/指針來記錄每層的最右邊的元素斤寂,邏輯是隨著內(nèi)層while循環(huán)的進(jìn)行,rightMostNode會(huì)逐漸指向右邊的元素揪惦,當(dāng)內(nèi)側(cè)while循環(huán)結(jié)束時(shí)遍搞,rightMostNode所指向的就是最右邊的元素,然后我們執(zhí)行result.add()器腋,就把值加入到集合中了溪猿。


  1. 637.二叉樹的層平均值
    本題是在層序遍歷的過程中,記錄每一層的節(jié)點(diǎn) 值的平均值纫塌,因此就需要求和诊县,然后同時(shí)每層的元素個(gè)數(shù)levelSize也要記錄,才方便后續(xù)求出平均值措左。完整代碼如下:
class Solution { // Java
    List<Double> result = new ArrayList<>();
    public List<Double> averageOfLevels(TreeNode root) {
        findAvg(root);
        return result;
    }

    public void findAvg(TreeNode root){
        if(root == null) return;
        
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);

        while(!queue.isEmpty()){
            int levelSize = queue.size();
            Double levelSum = 0.0; // record the sum in a level
            int levelNum = levelSize; // record the levelSize

            while(levelSize > 0){ // for循環(huán)替代的是這個(gè)while循環(huán)
                TreeNode cur = queue.poll();
                levelSum += cur.val; // get sum

                if(cur.left != null) queue.add(cur.left);
                if(cur.right != null) queue.add(cur.right);

                levelSize--;
            }

            Double avg = levelSum / levelNum; // get the average
            result.add(avg) ;
        }
    }
}

代碼的注釋很清晰的記錄了在基礎(chǔ)結(jié)構(gòu)上所做出的改動(dòng)依痊。其中值得說明的是,內(nèi)層while循環(huán)可以被for循環(huán)完美替代,每一個(gè)層序遍歷都是如此胸嘁,而在本題中瓶摆,使用for循環(huán)替代的好處是 可以不用定義一個(gè)levelNum來記錄levelSize了,因?yàn)槿绻褂脀hile循環(huán)的話性宏,levelSize就變成了循環(huán)控制因素群井,就會(huì)變化;而使用for循環(huán)毫胜,levelSize就不會(huì)變书斜。

            for (int i = 0; i < levelSize; i++) { // 替代上述while循環(huán)的for循環(huán)代碼
                TreeNode cur = queue.poll();
                levelSum += cur.val;

                if (cur.left != null) {
                    queue.add(cur.left);
                }

                if (cur.right != null) {
                    queue.add(cur.right);
                }
            }

  1. 429.N叉樹的層序遍歷
    這題就是把二叉樹換成了N叉樹,所以就是在遍歷每個(gè)節(jié)點(diǎn)的后面酵使,要把下一層節(jié)點(diǎn)加入到隊(duì)列中時(shí)荐吉,把左子樹、右子樹 這部分的代碼 替換成 一個(gè)for循環(huán)口渔,循環(huán)遍歷N叉樹節(jié)點(diǎn)的子節(jié)點(diǎn)數(shù)組样屠,然后把每個(gè)子節(jié)點(diǎn)都加入到隊(duì)列。
class Solution { // Java
    public List<List<Integer>> result = new LinkedList<>();
    public List<List<Integer>> levelOrder(Node root) {
        findLevelOrder(root);
        return result;
    }
    public void findLevelOrder(Node root){
        if(root == null) return;
        Queue<Node> queue = new LinkedList<>();

        queue.add(root);

        while(!queue.isEmpty()){
            int levelSize = queue.size();
            List<Integer> levelList = new ArrayList<>();


            while(levelSize > 0){
                Node tempNode = queue.poll();
                levelList.add(tempNode.val);
                for(Node n : tempNode.children){ // add all children of Node to the queue
                    if(n != null) queue.add(n);
                }
                levelSize--;
            }
            result.add(levelList);
        }
    }
}

  1. 515.在每個(gè)樹行中找最大值
    本題也比較好想搓劫,遍歷每層時(shí)瞧哟,加入一個(gè)變量一直記錄當(dāng)前層的最大值,當(dāng)一層遍歷結(jié)束時(shí)枪向,把最大值加入到結(jié)果集result中勤揩。完整代碼如下:
class Solution {
    public List<Integer> result = new ArrayList<>();
    public List<Integer> largestValues(TreeNode root) {
        findLargestValue(root);
        return result;
    }
    public void findLargestValue(TreeNode root){
        if(root == null) return;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        while(!queue.isEmpty()){
            int levelSize = queue.size();
            int largestNum = Integer.MIN_VALUE;

            while(levelSize > 0){
                TreeNode tempNode = queue.poll();
                largestNum = Math.max(largestNum, tempNode.val); // record the largest number
                if(tempNode.left != null) queue.offer(tempNode.left);
                if(tempNode.right != null) queue.offer(tempNode.right);

                levelSize--;
            }
            result.add(largestNum);
        }
    }
}

  1. 116.填充每個(gè)節(jié)點(diǎn)的下一個(gè)右側(cè)節(jié)點(diǎn)指針
    本題就是在處理每個(gè)節(jié)點(diǎn)時(shí),把它的next指針指向右側(cè)的節(jié)點(diǎn)秘蛔。對(duì)此我們采用的一個(gè)較為巧妙的做法是把彈出的當(dāng)前節(jié)點(diǎn)指向隊(duì)列queue中的頂部節(jié)點(diǎn)queue.peek();這是因?yàn)槲覀兊脑厥前凑諒淖蟮接抑饾u加入的陨亡,所以正好 對(duì)于當(dāng)前彈出的元素tempNode而言,他要指向的元素深员,也就是在二叉樹中位于他同層右邊一位的元素负蠕,就正好在隊(duì)列頂部。另外需要說明的一點(diǎn)是倦畅,本題有個(gè)特殊處理遮糖,那就是每層最右邊的元素沒人可指,但好在他原本的next值就是null叠赐,因此不需要額外處理欲账,只需處理前面 levelSize-1 個(gè)元素的next指針就好。
class Solution { //Java
    public Node connect(Node root) {
        connectNode(root);
        return root;
    }
    public void connectNode(Node root){
        if(root == null) return;
        Queue<Node> queue = new LinkedList<>();
        queue.offer(root);

        while(!queue.isEmpty()){
            int levelSize = queue.size();

            for(int i = 0; i < levelSize; i++){
                Node tempNode = queue.poll();
                if(i < levelSize - 1){ // 處理前l(fā)evelSize-1個(gè)元素就好
                    tempNode.next = queue.peek(); // 隊(duì)列頂端元素正好是下一個(gè)元素
                }
                if(tempNode.left != null) queue.offer(tempNode.left);
                if(tempNode.right != null) queue.offer(tempNode.right);
            }
        }
    }
}

  1. 117.填充每個(gè)節(jié)點(diǎn)的下一個(gè)右側(cè)節(jié)點(diǎn)指針I(yè)I
    本題與上一題LeetCode編號(hào)116的基本一樣赛不,唯一區(qū)別就在于116題中給出的二叉樹為完美二叉樹,而117題(本題)是普通二叉樹殿较。但在層序遍歷的代碼上是一樣的抓艳,這是因?yàn)槲覀儼言丶尤氲疥?duì)列中時(shí),就是按照每層的順序添加的,因此空位的地方自動(dòng)就被忽略了对供,所以代碼不需要修改即可直接使用氛濒。

總結(jié)

層序遍歷最重要的還是框架【┚埃框架主體結(jié)構(gòu)如下:
首先就是定義一個(gè)全局變量result來收集結(jié)果(大部分都需要收集結(jié)果,然后根據(jù)題目的要求可能是List<>鄙皇,也可能是List<List<>>.
然后就是定義一個(gè)方法來解決主要邏輯:
因?yàn)橛辛巳肿兞縼硎占Y(jié)果,所以一般參數(shù)只需傳入根節(jié)點(diǎn)root就好。
方法內(nèi)先判斷是否為空 if(root == null) return;
然后創(chuàng)建隊(duì)列Queue<TreeNode> queue = new LinkedList<>(); 隊(duì)列不能用ArrayList漱竖。
緊接著把root放入隊(duì)列中 queue.add(root); 此處用queue.offer(root);也行

然后是外層while循環(huán) while(!queue.isEmpty()){ 循環(huán)體1 }
在循環(huán)體1中 首先要做的就是記錄每層的大小levelSize,即當(dāng)前層有幾個(gè)節(jié)點(diǎn)悼吱。
然后根據(jù)需要可以創(chuàng)建一個(gè)數(shù)組/鏈表來收集本層信息。
所以 外層循環(huán)里所做事情的含義就是 對(duì)于每一層都要用到的信息可以提取到這里遇西。例如levelSize就是用來記錄每層的節(jié)點(diǎn)個(gè)數(shù),List<Integer>就是用來記錄每層的節(jié)點(diǎn)的值茄蚯。

內(nèi)層循環(huán)。
內(nèi)層循環(huán)可以是while循環(huán)皱碘,用levelSize > 0來控制尸执,但是每次循環(huán)后得levelSize-1。因此levelSize的值會(huì)隨著循環(huán)的進(jìn)行而變化褪贵。
內(nèi)層循環(huán)也可以是for循環(huán),用for(int i = 0, i < levelSize; i++){}來控制。好處就是levelSize不變歼培。具體使用看本文前半部分第四題查剖。
內(nèi)層循環(huán)的內(nèi)部邏輯則是需要根據(jù)題意來了,但大體框架是先做邏輯(例如把節(jié)點(diǎn)的值添加到集合中直砂,又或者是求得節(jié)點(diǎn)的值之和...)掘托;然后添加元素,對(duì)于二叉樹基本都是
if(tempNode.left != null) queue.add(tempNode.left);
if(tempNode.right != null) queue.add(tempNode.right); 這兩句。
稍微特殊點(diǎn)的就是那個(gè)N叉樹椅挣,但是原理也是一樣,都是遍歷所有的子節(jié)點(diǎn)然后添加。

最后就是在內(nèi)層循環(huán)結(jié)束后,但是外層循環(huán)結(jié)束前肌似。
在這里的話就是 如果需要把當(dāng)前層的結(jié)果作為一個(gè)集合收集起來受楼,就需要用全局變量result來收集它对雪。result.add(List);

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沛申,死亡現(xiàn)場離奇詭異褐隆,居然都是意外死亡污它,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門庶弃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衫贬,“玉大人,你說我怎么就攤上這事歇攻」坦撸” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵缴守,是天一觀的道長葬毫。 經(jīng)常有香客問我镇辉,道長,這世上最難降的妖魔是什么贴捡? 我笑而不...
    開封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任忽肛,我火速辦了婚禮,結(jié)果婚禮上烂斋,老公的妹妹穿的比我還像新娘屹逛。我一直安慰自己,他們只是感情好汛骂,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開白布罕模。 她就那樣靜靜地躺著,像睡著了一般帘瞭。 火紅的嫁衣襯著肌膚如雪淑掌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天蝶念,我揣著相機(jī)與錄音抛腕,去河邊找鬼。 笑死祸轮,一個(gè)胖子當(dāng)著我的面吹牛兽埃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播适袜,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼舷夺!你這毒婦竟也來了苦酱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤给猾,失蹤者是張志新(化名)和其女友劉穎疫萤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體敢伸,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扯饶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了池颈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尾序。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖躯砰,靈堂內(nèi)的尸體忽然破棺而出每币,到底是詐尸還是另有隱情,我是刑警寧澤琢歇,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布兰怠,位于F島的核電站梦鉴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏揭保。R本人自食惡果不足惜肥橙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望秸侣。 院中可真熱鬧快骗,春花似錦、人聲如沸塔次。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽励负。三九已至藕溅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間继榆,已是汗流浹背巾表。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留略吨,地道東北人集币。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像翠忠,于是被迫代替她去往敵國和親鞠苟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

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