上週經過一位小夥伴,加入了一個氛圍很好的小羣,人很少,可是你們保持着對知識的渴望,讓我很感動。java
我本身也有一個羣,人數也很少,可是能真正互動起來一塊兒學習,一塊兒進步的,仍是太少。因此,如今也在學習如何讓本身成爲更好的羣主,帶動羣活躍,帶動一個社羣活躍,帶動小夥伴們一塊兒進步,是個人願景。固然,也不否定如今不少羣友正在朝着積極向上的方向走着,我要作的,也是時刻保持對知識的渴望,作到「持續學習」。程序員
誰讓咱是一名優秀的程序員呢。上週日也學習了一遍遞歸,還經過一個二叉樹的例子來簡單介紹了下。我以前解決二叉樹相關的問題,基本上用的都是遞歸,結果那天分享的朋友用了隊列,讓我眼前一亮,原來程序的世界真是奇妙。算法
因此,思想碰撞真的是一件很開心的事情。你們在持續的學習,持續的交流中,會打開一些思惟定式,接納更多的方式,大家以爲呢?編程
114. 二叉樹展開爲鏈表
(https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/
)數據結構
題目描述:給定一個二叉樹,原地將它展開爲鏈表。數據結構和算法
例如,給定二叉樹學習
示例1:指針
1 / \ 2 5 / \ \ 3 4 6
將其展開爲:code
1 \ 2 \ 3 \ 4 \ 5 \ 6
本文題解參考地址:https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by--26/
遞歸
題目其實就是將二叉樹經過右指針,組成一個鏈表。
從例子上能夠看出,其實就是讓咱們把二叉樹,經過先序遍歷展現出來。因此咱們首先想到的是能不能用先序遍歷的方式,每遍歷一個節點,就將上一個節點的右指針更新爲當前節點。
先序遍歷的順序是 1->2->3->4->5->6,以下:
遍歷到2
,把1
的右指針指向2
,即變成 1->2 3 4 5 6
遍歷到3
,把2
的右指針指向3
,即變成 1->2->3 4 5 6
理想情況下,以此類推便可。
可是,若是咱們把1
的右指針指向2
,那麼這時候1
本來的右節點就丟失了,也就是咱們後續找不到5
這個節點。
因此,又引發了咱們的思考,如何才能不讓5
丟失呢?後序遍歷能夠嗎?
也就是咱們依次遍歷6 5 4 3 2 1
,而後每遍歷一個節點就將當前節點的右指針更新爲上一個節點,以下:
遍歷到5
,把5
的右指針指向6
,即變成6 <- 5 4 3 2 1
遍歷到4
,把4
的右指針指向5
,即變成6 <- 5 <- 4 3 2 1
以此類推,由於咱們更新當前右指針的時候,當前節點的右節點已經訪問過了,因此就不會存在丟失節點的問題。
把這個轉變成後序遍歷,遍歷順序就是 右子樹 -> 左子樹 -> 根節點
// 將二叉樹構建完成 public static void main(String[] args) { TreeNode treeNode = new TreeNode(1); treeNode.left = new TreeNode(2); treeNode.left.left = new TreeNode(3); treeNode.left.right = new TreeNode(4); treeNode.right = new TreeNode(5); treeNode.right.right = new TreeNode(6); flattern(treeNode); }
/** * * @Title : * @Description: 後續遍歷 * @param treeNode * @return :void * @throws */ public static void flattern(TreeNode root) { Stack<TreeNode> treeNodes = new Stack<>(); TreeNode current = root; TreeNode preview = null; while (current != null || !treeNodes.isEmpty()) { while (current != null) { // 添加根節點 treeNodes.push(current); // 添加右節點 current = current.right; } // 已經訪問到最右邊的節點 current = treeNodes.peek(); // 當右節點已經被訪問過或者左節點不存在的狀況,就去訪問根節點 if (current.left == null || current.left == preview) { treeNodes.pop(); current.right = preview; current.left = null; preview = current; current = null; } else { current = current.left; } } }
在介紹着後序遍歷的時候,咱們先用先序遍歷的例子以及缺陷,來講明爲何咱們選擇後序遍歷。那麼,就必定不能用先序遍歷了嗎?顯然,答案是不對的。
有一種特殊的先序遍歷,提早將右節點保存到棧中,咱們利用這種遍歷方式就能夠防止右節點的丟失。由於棧是先進後出,因此咱們先將右節點入棧。
再根據上面先序遍歷的分析,由於咱們用棧保存了右孩子,因此不須要擔憂右孩子丟失了。用一個 pre 變量保存上次遍歷的節點便可。
public static void flatten1(TreeNode root) { if (root == null){ return; } Stack<TreeNode> s = new Stack<TreeNode>(); s.push(root); TreeNode pre = null; while (!s.isEmpty()) { TreeNode temp = s.pop(); if(pre!=null){ pre.right = temp; pre.left = null; } if (temp.right != null){ s.push(temp.right); } if (temp.left != null){ s.push(temp.left); } pre = temp; } }
二叉樹,是一顆神奇的樹,理論上咱們均可以經過先序、中序、後續遍從來拆解他,獲得咱們想要的結果,在作題的時候也是如此。
可是真正體現到編程的世界裏,仍是和理論作題有一點不一樣,編程須要咱們用機器語言是實現,去思考,去打通咱們算法的任督二脈,這樣就是LeetCode存在的魅力,他是一個氛圍很好的社區,擁有它,就擁有了算法的世界,擁有了進階的可能。
安利了LeetCode,安利下本身的號,儘可能作到每週一題,分模塊的學習。小編最大的後悔就是沒能在大學學好數據結構和算法這門課,如今吃虧了,吃大虧了。好在萬變不離其宗,只要經過本身的勤奮,沒有什麼是不可能的。
加油,奔跑吧兄弟們!
做者:Dimple
公衆號:奔跑吧攻城獅
本文由博客一文多發平臺 OpenWrite 發佈!