From c492890cbe43cb516f44319d13e294d58ba01428 Mon Sep 17 00:00:00 2001 From: chefyuan Date: Mon, 28 Jun 2021 19:06:22 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BA=8C=E5=8F=89=E6=A0=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- ...‰æ ‘中åºé历.md => 二å‰æ ‘中åºé历(Morris).md} | 53 ----- .../二å‰æ ‘/二å‰æ ‘中åºé历(迭代).md | 54 +++++ ...‰æ ‘çš„åŽç»­é历.md => 二å‰æ ‘çš„åŽç»­é历 (迭代).md} | 0 .../二å‰æ ‘/二å‰æ ‘çš„åŽç»­é历(Morris).md | 207 ++++++++++++++++++ 5 files changed, 263 insertions(+), 54 deletions(-) rename animation-simulation/二å‰æ ‘/{二å‰æ ‘中åºé历.md => 二å‰æ ‘中åºé历(Morris).md} (58%) create mode 100644 animation-simulation/二å‰æ ‘/二å‰æ ‘中åºé历(迭代).md rename animation-simulation/二å‰æ ‘/{二å‰æ ‘çš„åŽç»­é历.md => 二å‰æ ‘çš„åŽç»­é历 (迭代).md} (100%) create mode 100644 animation-simulation/二å‰æ ‘/二å‰æ ‘çš„åŽç»­é历(Morris).md diff --git a/README.md b/README.md index 2649772..ed546f5 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,8 @@ ### ðŸºäºŒå‰æ ‘ -- [ã€åŠ¨ç”»æ¨¡æ‹Ÿã€‘å‰åºé历(迭代+Morris)](https://github.com/chefyuan/algorithm-base/blob/main/animation-simulation/%E4%BA%8C%E5%8F%89%E6%A0%91/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86(%E6%A0%88).md) +- [ã€åŠ¨ç”»æ¨¡æ‹Ÿã€‘å‰åºé历(迭代)](https://github.com/chefyuan/algorithm-base/blob/main/animation-simulation/%E4%BA%8C%E5%8F%89%E6%A0%91/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86(%E6%A0%88).md) +- [ã€åŠ¨ç”»æ¨¡æ‹Ÿã€‘å‰åºé历(Morris)](https://github.com/chefyuan/algorithm-base/blob/main/animation-simulation/%E4%BA%8C%E5%8F%89%E6%A0%91/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86(Morris).md) - ã€åŠ¨ç”»æ¨¡æ‹Ÿã€‘中åºé历(迭代+Morris) - ã€åŠ¨ç”»æ¨¡æ‹Ÿã€‘åŽåºé历(迭代+Morris) diff --git a/animation-simulation/二å‰æ ‘/二å‰æ ‘中åºé历.md b/animation-simulation/二å‰æ ‘/二å‰æ ‘中åºé历(Morris).md similarity index 58% rename from animation-simulation/二å‰æ ‘/二å‰æ ‘中åºé历.md rename to animation-simulation/二å‰æ ‘/二å‰æ ‘中åºé历(Morris).md index d062dbd..3c6ff79 100644 --- a/animation-simulation/二å‰æ ‘/二å‰æ ‘中åºé历.md +++ b/animation-simulation/二å‰æ ‘/二å‰æ ‘中åºé历(Morris).md @@ -1,56 +1,3 @@ -哈喽大家好,我是厨å­ï¼Œä¹‹å‰æˆ‘们说了二å‰æ ‘å‰åºé历的迭代法和 Morris,今天咱们写一下中åºé历的迭代法和 Morris。 - -> 注:数æ®ç»“构掌æ¡ä¸ç†Ÿç»ƒçš„åŒå­¦ï¼Œé˜…读该文章之å‰ï¼Œå¯ä»¥å…ˆé˜…读这两篇文章,二å‰æ ‘基础,å‰åºé历å¦å¤–喜欢电脑阅读的åŒå­¦ï¼Œå¯ä»¥åœ¨å°å±‹åŽå°å›žå¤ä»“库地å€ï¼ŒèŽ·å– Github 链接进行阅读。 - -中åºé历的顺åºæ˜¯, `对于树中的æŸèŠ‚点,å…ˆé历该节点的左å­æ ‘, 然åŽå†é历该节点, 最åŽé历其å³å­æ ‘`。è€è§„矩,上动画,我们先通过动画回忆一下二å‰æ ‘的中åºé历。 - -![中åºé历](https://cdn.jsdelivr.net/gh/tan45du/test@master/photo/中åºé历.7gct7ztck8k0.gif) - -注:二å‰æ ‘基础总结大家å¯ä»¥é˜…读这篇文章,点我。 - -## 迭代法 - -我们二å‰æ ‘的中åºé历迭代法和å‰åºé历是一样的,都是借助栈æ¥å¸®åŠ©æˆ‘们完æˆã€‚ - -我们结åˆåŠ¨ç”»æ€è€ƒä¸€ä¸‹ï¼Œè¯¥å¦‚何借助栈æ¥å®žçŽ°å‘¢ï¼Ÿ - -我们æ¥çœ‹ä¸‹é¢è¿™ä¸ªåŠ¨ç”»ã€‚ - -![在这里æ’入图片æè¿°](https://img-blog.csdnimg.cn/20210608010104232.gif) - -用栈实现的二å‰æ ‘的中åºé历有两个关键的地方。 - -- 指针ä¸æ–­å‘节点的左孩å­ç§»åŠ¨ï¼Œä¸ºäº†æ‰¾åˆ°æˆ‘们当å‰éœ€è¦é历的节点。途中ä¸æ–­æ‰§è¡Œå…¥æ ˆæ“作 -- 当指针为空时,则开始出栈,并将指针指å‘出栈节点的å³å­©å­ã€‚ - -这两个关键点也很容易ç†è§£ï¼ŒæŒ‡é’ˆä¸æ–­å‘左孩å­ç§»åŠ¨ï¼Œæ˜¯ä¸ºäº†æ‰¾åˆ°æˆ‘们此时需è¦èŠ‚点。然åŽå½“指针指å‘空时,则说明我们此时已ç»æ‰¾åˆ°è¯¥èŠ‚点,执行出栈æ“作,并将其值存入 list å³å¯ï¼Œå¦å¤–我们需è¦å°†æŒ‡é’ˆæŒ‡å‘出栈节点的å³å­©å­ï¼Œè¿­ä»£æ‰§è¡Œä¸Šè¯‰æ“作。 - -大家是ä¸æ˜¯å·²ç»çŸ¥é“怎么写啦,下é¢æˆ‘们看代ç å§ã€‚ - -```java -class Solution { - public List inorderTraversal(TreeNode root) { - List arr = new ArrayList<>(); - TreeNode cur = new TreeNode(-1); - cur = root; - Stack stack = new Stack<>(); - while (!stack.isEmpty() || cur != null) { - //找到当å‰åº”该é历的那个节点 - while (cur != null) { - stack.push(cur); - cur = cur.left; - } - //此时指针指å‘空,也就是没有左å­èŠ‚点,则开始执行出栈æ“作 - TreeNode temp = stack.pop(); - arr.add(temp.val); - //指å‘å³å­èŠ‚点 - cur = temp.right; - } - return arr; - } -} -``` - ### **Morris** 我们之å‰è¯´è¿‡ï¼Œå‰åºé历的 Morris 方法,如果已ç»æŽŒæ¡ï¼Œä»Šå¤©ä¸­åºé历的 Morris 方法也就没有什么难度,仅仅修改了一丢丢。 diff --git a/animation-simulation/二å‰æ ‘/二å‰æ ‘中åºé历(迭代).md b/animation-simulation/二å‰æ ‘/二å‰æ ‘中åºé历(迭代).md new file mode 100644 index 0000000..92fa27d --- /dev/null +++ b/animation-simulation/二å‰æ ‘/二å‰æ ‘中åºé历(迭代).md @@ -0,0 +1,54 @@ +哈喽大家好,我是厨å­ï¼Œä¹‹å‰æˆ‘们说了二å‰æ ‘å‰åºé历的迭代法和 Morris,今天咱们写一下中åºé历的迭代法和 Morris。 + +> 注:数æ®ç»“构掌æ¡ä¸ç†Ÿç»ƒçš„åŒå­¦ï¼Œé˜…读该文章之å‰ï¼Œå¯ä»¥å…ˆé˜…读这两篇文章,二å‰æ ‘基础,å‰åºé历å¦å¤–喜欢电脑阅读的åŒå­¦ï¼Œå¯ä»¥åœ¨å°å±‹åŽå°å›žå¤ä»“库地å€ï¼ŒèŽ·å– Github 链接进行阅读。 + +中åºé历的顺åºæ˜¯, `对于树中的æŸèŠ‚点,å…ˆé历该节点的左å­æ ‘, 然åŽå†é历该节点, 最åŽé历其å³å­æ ‘`。è€è§„矩,上动画,我们先通过动画回忆一下二å‰æ ‘的中åºé历。 + +![中åºé历](https://cdn.jsdelivr.net/gh/tan45du/test@master/photo/中åºé历.7gct7ztck8k0.gif) + +注:二å‰æ ‘基础总结大家å¯ä»¥é˜…读这篇文章,点我。 + +## 迭代法 + +我们二å‰æ ‘的中åºé历迭代法和å‰åºé历是一样的,都是借助栈æ¥å¸®åŠ©æˆ‘们完æˆã€‚ + +我们结åˆåŠ¨ç”»æ€è€ƒä¸€ä¸‹ï¼Œè¯¥å¦‚何借助栈æ¥å®žçŽ°å‘¢ï¼Ÿ + +我们æ¥çœ‹ä¸‹é¢è¿™ä¸ªåŠ¨ç”»ã€‚ + +![在这里æ’入图片æè¿°](https://img-blog.csdnimg.cn/20210608010104232.gif) + +用栈实现的二å‰æ ‘的中åºé历有两个关键的地方。 + +- 指针ä¸æ–­å‘节点的左孩å­ç§»åŠ¨ï¼Œä¸ºäº†æ‰¾åˆ°æˆ‘们当å‰éœ€è¦é历的节点。途中ä¸æ–­æ‰§è¡Œå…¥æ ˆæ“作 +- 当指针为空时,则开始出栈,并将指针指å‘出栈节点的å³å­©å­ã€‚ + +这两个关键点也很容易ç†è§£ï¼ŒæŒ‡é’ˆä¸æ–­å‘左孩å­ç§»åŠ¨ï¼Œæ˜¯ä¸ºäº†æ‰¾åˆ°æˆ‘们此时需è¦èŠ‚点。然åŽå½“指针指å‘空时,则说明我们此时已ç»æ‰¾åˆ°è¯¥èŠ‚点,执行出栈æ“作,并将其值存入 list å³å¯ï¼Œå¦å¤–我们需è¦å°†æŒ‡é’ˆæŒ‡å‘出栈节点的å³å­©å­ï¼Œè¿­ä»£æ‰§è¡Œä¸Šè¯‰æ“作。 + +大家是ä¸æ˜¯å·²ç»çŸ¥é“怎么写啦,下é¢æˆ‘们看代ç å§ã€‚ + +```java +class Solution { + public List inorderTraversal(TreeNode root) { + List arr = new ArrayList<>(); + TreeNode cur = new TreeNode(-1); + cur = root; + Stack stack = new Stack<>(); + while (!stack.isEmpty() || cur != null) { + //找到当å‰åº”该é历的那个节点 + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + //此时指针指å‘空,也就是没有左å­èŠ‚点,则开始执行出栈æ“作 + TreeNode temp = stack.pop(); + arr.add(temp.val); + //指å‘å³å­èŠ‚点 + cur = temp.right; + } + return arr; + } +} +``` + +### \ No newline at end of file diff --git a/animation-simulation/二å‰æ ‘/二å‰æ ‘çš„åŽç»­é历.md b/animation-simulation/二å‰æ ‘/二å‰æ ‘çš„åŽç»­é历 (迭代).md similarity index 100% rename from animation-simulation/二å‰æ ‘/二å‰æ ‘çš„åŽç»­é历.md rename to animation-simulation/二å‰æ ‘/二å‰æ ‘çš„åŽç»­é历 (迭代).md diff --git a/animation-simulation/二å‰æ ‘/二å‰æ ‘çš„åŽç»­é历(Morris).md b/animation-simulation/二å‰æ ‘/二å‰æ ‘çš„åŽç»­é历(Morris).md new file mode 100644 index 0000000..0332a5f --- /dev/null +++ b/animation-simulation/二å‰æ ‘/二å‰æ ‘çš„åŽç»­é历(Morris).md @@ -0,0 +1,207 @@ +之å‰ç»™å¤§å®¶ä»‹ç»äº†äºŒå‰æ ‘çš„[å‰åºé历](),[中åºé历]()的迭代法和 Morris 方法,今天咱们æ¥è¯´ä¸€ä¸‹äºŒå‰åŽåºéåŽ†çš„è¿­ä»£æ³•åŠ Morris 方法。 + +注:阅读该文章å‰ï¼Œå»ºè®®å„ä½å…ˆé˜…读之å‰çš„三篇文章,对该文章的ç†è§£æœ‰å¾ˆå¤§å¸®åŠ©ã€‚ + +## 迭代 + +åŽåºé历的相比å‰ä¸¤ç§æ–¹æ³•ï¼Œéš¾ç†è§£äº†ä¸€äº›ï¼Œæ‰€ä»¥è¿™é‡Œæˆ‘们需è¦è®¤çœŸæ€è€ƒä¸€ä¸‹ï¼Œæ¯ä¸€è¡Œçš„代ç çš„作用。 + +我们先æ¥å¤ä¹ ä¸€ä¸‹ï¼ŒäºŒå‰æ ‘çš„åŽåºé历 + +![](https://cdn.jsdelivr.net/gh/tan45du/test@master/photo/åŽåºé历.2bx6qccr1q1w.gif) + +我们知é“åŽåºé历的顺åºæ˜¯,` 对于树中的æŸèŠ‚点, å…ˆé历该节点的左å­æ ‘, å†é历其å³å­æ ‘, 最åŽé历该节点`。 + +那么我们如何利用栈æ¥è§£å†³å‘¢ï¼Ÿ + +我们直接æ¥çœ‹åŠ¨ç”»ï¼Œçœ‹åŠ¨ç”»ä¹‹å‰ï¼Œä½†æ˜¯æˆ‘们`需è¦å¸¦ç€é—®é¢˜çœ‹åŠ¨ç”»`,问题æžæ‡‚之åŽä¹Ÿå°±æžå®šäº†åŽåºé历。 + +1.动画中的橙色指针å‘挥了什么作用 + +2.为什么动画中的æŸèŠ‚点,为什么出栈åŽåˆå…¥æ ˆå‘¢? + +好啦,下é¢æˆ‘们看动画å§ï¼ + +![åŽåºé历迭代](https://img-blog.csdnimg.cn/20210622160754912.gif) + +相信大家看完动画之åŽï¼Œä¹Ÿèƒ½å¤Ÿå‘现其中规律。 + +我们æ¥å¯¹å…¶ä¸­ä¹‹å‰æ出的问题进行解答 + +1.动画中的橙色箭头的作用? + +> 用æ¥å®šä½ä½ä¸Šä¸€ä¸ªè®¿é—®èŠ‚ç‚¹ï¼Œè¿™æ ·æˆ‘ä»¬å°±çŸ¥é“ cur 节点的 right 节点是å¦è¢«è®¿é—®ï¼Œå¦‚果被访问,我们则需è¦é历 cur 节点。 + +2.为什么有的节点出栈åŽåˆå…¥æ ˆäº†å‘¢ï¼Ÿ + +> 出栈åˆå…¥æ ˆçš„原因是,我们å‘现 cur 节点的 right ä¸ä¸º null ,并且 cur.right 也没有被访问过。因为 `cur.right != preNode `,所以当å‰æˆ‘们还ä¸èƒ½å¤Ÿé历该节点,应该先é历其å³å­æ ‘中的节点。 +> +> 所以我们将其入栈åŽï¼Œç„¶åŽ`cur = cur.right` + +```java +class Solution { + public List postorderTraversal(TreeNode root) { + Stack stack = new Stack<>(); + List list = new ArrayList<>(); + TreeNode cur = root; + //这个用æ¥è®°å½•å‰ä¸€ä¸ªè®¿é—®çš„节点,也就是橙色箭头 + TreeNode preNode = null; + while (cur != null || !stack.isEmpty()) { + //和之å‰å†™çš„中åºä¸€è‡´ + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + //1.出栈,å¯ä»¥æƒ³ä¸€ä¸‹ï¼Œè¿™ä¸€æ­¥çš„原因。 + cur = stack.pop(); + //2.if 里的判断语å¥æœ‰ä»€ä¹ˆå«ä¹‰ï¼Ÿ + if (cur.right == null || cur.right == preNode) { + list.add(cur.val); + //更新下 preNode,也就是定ä½ä½ä¸Šä¸€ä¸ªè®¿é—®èŠ‚点。 + preNode = cur; + cur = null; + } else { + //3.å†æ¬¡åŽ‹å…¥æ ˆï¼Œå’Œä¸Šé¢é‚£æ¡ 1 的关系? + stack.push(cur); + cur = cur.right; + } + } + return list; + } +} +``` + +当然也å¯ä»¥ä¿®æ”¹ä¸‹ä»£ç é€»è¾‘å°† `cur = stack.pop()` æ”¹æˆ `cur = stack.peek()`,下é¢å†ä¿®æ”¹ä¸€ä¸¤è¡Œä»£ç ä¹Ÿå¯ä»¥å®žçŽ°ï¼Œè¿™é‡Œè¿™æ ·å†™æ˜¯æ–¹ä¾¿åŠ¨ç”»æ¨¡æ‹Ÿï¼Œå¤§å®¶å¯ä»¥éšæ„å‘挥。 + +时间å¤æ‚度 O(n), 空间å¤æ‚度O(n) + +这里二å‰æ ‘的三ç§è¿­ä»£æ–¹å¼åˆ°è¿™é‡Œå°±ç»“æŸå•¦ï¼Œå¤§å®¶å¯ä»¥è¿›è¡Œå½’纳总结,三ç§é历方å¼å¤§åŒå°å¼‚,建议å„ä½ï¼ŒæŽŒæ¡ä¹‹åŽï¼Œè‡ªå·±æ‰‹æ’•ä¸€ä¸‹ï¼Œä»Žæ­å»ºäºŒå‰æ ‘开始。 + +å¦å¤–大家也å¯ä»¥çœ‹ä¸‹ Carl 哥的这篇文章,迭代é历的å¦ä¸€ç§å®žçŽ°æ–¹å¼ã€‚ + +> https://leetcode-cn.com/problems/binary-tree-postorder-traversal/solution/bang-ni-dui-er-cha-shu-bu-zai-mi-mang-che-di-chi-t/ + +好啦,下é¢æˆ‘们看下åŽåºé历的 Morris 方法。 + +## Morris + +åŽåºé历的 Morris 方法也比之å‰ä¸¤ç§ä»£ç ç¨å¾®é•¿ä¸€äº›ï¼Œçœ‹ç€æŒºå”¬äºº,其实ä¸éš¾ï¼Œå’Œæˆ‘们之å‰è¯´çš„没差多少。下é¢æˆ‘们一起æ¥å¹²æŽ‰å®ƒå§ã€‚ + +我们先æ¥å¤ä¹ ä¸‹ä¹‹å‰è¯´è¿‡çš„[中åºé历](),è§ä¸‹å›¾ã€‚ + +![](https://img-blog.csdnimg.cn/20210622155624486.gif) + +å¦å¤–我们æ¥å¯¹æ¯”下,中åºé历和åŽåºé历的 Morris 方法,代ç æœ‰å“ªé‡Œä¸åŒã€‚ + +![在这里æ’入图片æè¿°](https://img-blog.csdnimg.cn/20210622142148928.png) + +由上图å¯çŸ¥ï¼Œä»…仅有三处ä¸åŒï¼ŒåŽåºé历里少了 `list.add()`,多了一个函数` postMorris() ` ,那åŽåºé历的 list.add() 肯定是在 postMorris 函数中的。所以我们æžæ‡‚了 postMorris 函数,也就æžæ‡‚了åŽåºé历的 Morris 方法(默认大家看了之å‰çš„文章,没有看过的åŒå­¦ï¼Œå¯ä»¥ç‚¹å‡»æ–‡é¦–的链接) + +下é¢æˆ‘们一起æ¥å‰–æžä¸‹ postMorris 函数.代ç å¦‚下 + +```java +public void postMorris(TreeNode root) { + //å转转链表,详情看下方图片 + TreeNode reverseNode = reverseList(root); + //é历链表 + TreeNode cur = reverseNode; + while (cur != null) { + list.add(cur.val); + cur = cur.right; + } + //åè½¬å›žæ¥ + reverseList(reverseNode); + } + +//å转链表 +public TreeNode reverseList(TreeNode head) { + TreeNode cur = head; + TreeNode pre = null; + while (cur != null) { + TreeNode next = cur.right; + cur.right = pre; + pre = cur; + cur = next; + } + return pre; + } +``` + +上é¢çš„代ç ï¼Œæ˜¯ä¸æ˜¯è´¼ç†Ÿæ‚‰ï¼Œå’Œæˆ‘们的倒åºè¾“出链表一致,步骤为,å转链表,é历链表,将链表å转回原样。åªä¸è¿‡æˆ‘们将 ListNode.next 写æˆäº† TreeNode.right 将树中的é历å³å­èŠ‚点的路线,看æˆäº†ä¸€ä¸ªé“¾è¡¨ï¼Œè§ä¸‹å›¾ã€‚ + +![](https://img-blog.csdnimg.cn/20210622145335283.png) + +上图中的一个绿色虚线,代表一个链表,我们根æ®åºå·è¿›è¡Œå€’åºé历,看下是什么情况 + +![在这里æ’入图片æè¿°](https://img-blog.csdnimg.cn/20210622145805876.png) + +![在这里æ’入图片æè¿°](https://img-blog.csdnimg.cn/20210622145846117.png) + +到这å—是ä¸æ˜¯å°±æ•´æ‡‚å•¦ï¼Œæ‰“å®Œæ”¶å·¥ï¼ + +```java +class Solution { + List list; + public List postorderTraversal(TreeNode root) { + list = new ArrayList<>(); + if (root == null) { + return list; + } + TreeNode p1 = root; + TreeNode p2 = null; + while (p1 != null) { + p2 = p1.left; + if (p2 != null) { + while (p2.right != null && p2.right != p1) { + p2 = p2.right; + } + if (p2.right == null) { + p2.right = p1; + p1 = p1.left; + continue; + } else { + p2.right = null; + postMorris(p1.left); + } + } + p1 = p1.right; + } + //以根节点为起点的链表 + postMorris(root); + return list; + } + public void postMorris(TreeNode root) { + //翻转链表 + TreeNode reverseNode = reverseList(root); + //从åŽå¾€å‰é历 + TreeNode cur = reverseNode; + while (cur != null) { + list.add(cur.val); + cur = cur.right; + } + //ç¿»è½¬å›žæ¥ + reverseList(reverseNode); + } + public TreeNode reverseList(TreeNode head) { + TreeNode cur = head; + TreeNode pre = null; + while (cur != null) { + TreeNode next = cur.right; + cur.right = pre; + pre = cur; + cur = next; + } + return pre; + } + +} +``` + +时间å¤æ‚度 O(n)空间å¤æ‚度 O(1) + +总结:åŽåºé历比起å‰åºå’Œä¸­åºç¨å¾®å¤æ‚了一些,所以我们解题的时候,需è¦å¥½å¥½æ³¨æ„一下,迭代法的核心是利用一个指针æ¥å®šä½æˆ‘们上一个é历的节点,Morris 的核心是,将æŸèŠ‚点的å³å­èŠ‚点,看æˆæ˜¯ä¸€æ¡é“¾è¡¨ï¼Œè¿›è¡Œåå‘é历。 + +好啦,今天就唠到这å§ï¼Œæ‹œäº†ä¸ªæ‹œã€‚ + + +