福哥答案2020-08-26:

方法 1:迭代

算法

从根节点开始,每次迭代弹出当前栈顶元素,并将其孩子节点压入栈中,先压右孩子再压左孩子。

在这个算法中,输出到最终结果的顺序按照 Top->Bottom 和 Left->Right,符合前序遍历的顺序。

算法复杂度

时间复杂度:访问每个节点恰好一次,时间复杂度为 O(N) ,其中 N 是节点的个数,也就是树的大小。

空间复杂度:取决于树的结构,最坏情况存储整棵树,因此空间复杂度是 O(N)。

方法 2:莫里斯遍历

方法基于 莫里斯的文章,可以优化空间复杂度。算法不会使用额外空间,只需要保存最终的输出结果。如果实时输出结果,那么空间复杂度是 O(1)。

算法

算法的思路是从当前节点向下访问先序遍历的前驱节点,每个前驱节点都恰好被访问两次。

首先从当前节点开始,向左孩子走一步然后沿着右孩子一直向下访问,直到到达一个叶子节点(当前节点的中序遍历前驱节点),所以我们更新输出并建立一条伪边 predecessor.Right = root 更新这个前驱的下一个点。如果我们第二次访问到前驱节点,由于已经指向了当前节点,我们移除伪边并移动到下一个顶点。

如果第一步向左的移动不存在,就直接更新输出并向右移动。

算法复杂度

时间复杂度:每个前驱恰好访问两次,因此复杂度是 O(N),其中 N 是顶点的个数,也就是树的大小。

空间复杂度:我们在计算中不需要额外空间,但是输出需要包含 N 个元素,因此空间复杂度为 O(N)。

代码用golang编写,如下:

package test34_preordertraversalimport (    "fmt"    "testing")//https://leetcode-cn.com/problems/binary-tree-preorder-traversal/solution/er-cha-shu-de-qian-xu-bian-li-by-leetcode///go test -v -test.run TestPreorderTraversalfunc TestPreorderTraversal(t *testing.T) {    root := &TreeNode{}    root.Val = 1    root.Left = &TreeNode{}    root.Left.Val = 2    root.Right = &TreeNode{}    root.Right.Val = 3    root.Left.Left = &TreeNode{}    root.Left.Left.Val = 4    root.Left.Right = &TreeNode{}    root.Left.Right.Val = 5    root.Right.Left = &TreeNode{}    root.Right.Left.Val = 6    root.Right.Right = &TreeNode{}    root.Right.Right.Val = 7    fmt.Println(preorderTraversal1(root))    fmt.Println(preorderTraversal2(root))}//Definition for a binary tree node.type TreeNode struct {    Val   int    Left  *TreeNode    Right *TreeNode}//方法 1:迭代//从根节点开始,每次迭代弹出当前栈顶元素,并将其孩子节点压入栈中,先压右孩子再压左孩子。//在这个算法中,输出到最终结果的顺序按照 Top->Bottom 和 Left->Right,符合前序遍历的顺序。//算法复杂度//时间复杂度:访问每个节点恰好一次,时间复杂度为 O(N) ,其中 N 是节点的个数,也就是树的大小。//空间复杂度:取决于树的结构,最坏情况存储整棵树,因此空间复杂度是 O(N)。func preorderTraversal1(root *TreeNode) []int {    stack := make([]*TreeNode, 0)    output := make([]int, 0)    if root == nil {        return output    }    //push 根    stack = append(stack, root)    for len(stack) > 0 {        //pop        node := stack[len(stack)-1]        stack = stack[0 : len(stack)-1]        output = append(output, node.Val)        if node.Right != nil {            //push右            stack = append(stack, node.Right)        }        if node.Left != nil {            //push左            stack = append(stack, node.Left)        }    }    return output}//方法 2:莫里斯遍历//方法基于 莫里斯的文章,可以优化空间复杂度。算法不会使用额外空间,只需要保存最终的输出结果。如果实时输出结果,那么空间复杂度是 O(1)。//算法//算法的思路是从当前节点向下访问先序遍历的前驱节点,每个前驱节点都恰好被访问两次。//首先从当前节点开始,向左孩子走一步然后沿着右孩子一直向下访问,直到到达一个叶子节点(当前节点的中序遍历前驱节点),所以我们更新输出并建立一条伪边 predecessor.Right = root 更新这个前驱的下一个点。如果我们第二次访问到前驱节点,由于已经指向了当前节点,我们移除伪边并移动到下一个顶点。//如果第一步向左的移动不存在,就直接更新输出并向右移动。//算法复杂度//时间复杂度:每个前驱恰好访问两次,因此复杂度是 O(N),其中 N 是顶点的个数,也就是树的大小。//空间复杂度:我们在计算中不需要额外空间,但是输出需要包含 N 个元素,因此空间复杂度为 O(N)。func preorderTraversal2(root *TreeNode) []int {    output := make([]int, 0)    node := root    for node != nil {        if node.Left == nil {            //push根            output = append(output, node.Val)            //右            node = node.Right        } else {            predecessor := node.Left            for predecessor.Right != nil && predecessor.Right != node {                predecessor = predecessor.Right            }            if predecessor.Right == nil {                output = append(output, node.Val)                predecessor.Right = node                node = node.Left            } else {                predecessor.Right = nil                node = node.Right            }        }    }    return output}

敲命令 go test -v -test.run TestPreorderTraversal ,执行结果如下:

js递归遍历json树_2020-08-26:裸写算法:树的非递归先序遍历相关推荐

  1. 一步一步写算法(之非递归排序)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 在上面一篇博客当中,我们发现普通查找和排序查找的性能差别很大.作为一个100万的数据,如果使用 ...

  2. 简述树的深度优先及广度优先遍历算法,并说明非递归实现?

    深度优先遍历二叉树. 1. 中序遍历(LDR)的递归算法: 若二叉树为空,则算法结束:否则: 中序遍历根结点的左子树: 访问根结点: 中序遍历根结点的右子树. 2. 前序遍历(DLR)的递归算法: 若 ...

  3. 数据结构与算法(java):树-二叉树(二叉查找树(BST)、线索化二叉树、哈夫曼树、平衡二叉树【AVL】、二叉树的前中后序遍历)

    二叉树 1.定义 二叉树 就是度不超过2的树(每个结点最多只有两个子结点).如图 2.特殊二叉树 满二叉树 当二叉树的每一个层的结点树都达到最大值,则这个二叉树就是满二叉树. 完全二叉树 叶结点只能出 ...

  4. 算法 - 二分查找(非递归实现二分查找)

    package Algorithm.binarysearchnorecursion;public class BinaaySearchNoRecur {public static void main( ...

  5. 快速排序非递归算法c语言实现,数据结构与算法----3.5 非递归的快速排序方法

    [c++]代码库#include using namespace std; #include using namespace std; typedef int KeyType; struct LEle ...

  6. [算法] 已知前序和后序遍历,建立二叉树

    结点结构 typedef struct Node{char data;struct Node *left;struct Node *right; }Node; 已知前序和后序遍历,建立二叉树(不唯一, ...

  7. [算法] 已知前序和中序遍历,建立二叉树

    结点结构 typedef struct Node{char data;struct Node *left;struct Node *right; }Node; 已知前序和中序遍历顺序,建立二叉树 问题 ...

  8. AVL树的理解及自写AVL树

    AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增加和删除可能需要通过一次或多 ...

  9. 非递归创建排序二叉树

    非递归创建排序二叉树 今天,博主写了关于一个非递归创建排序二叉树的算法,平时,我们常见的都是递归创建排序二叉树,但是递归的空间复杂度也更高为o(n),相反,非递归的空间复杂度则为o(1),下面是代码, ...

最新文章

  1. ASP.NET小知识
  2. mysql 5.0 乱码,解决MySQL 5.0.16的乱码问题
  3. asp.net用url重写URLReWriter实现任意二级域名(续)
  4. csv phoenix 导入_phoenix学习
  5. sql truncate_如何在SQL Delete和SQL Truncate语句后使用数据库备份恢复数据
  6. html在线发布ipa文件在哪里,打包ipa文件
  7. 最全的iOS真机调试教程(证书生成等)
  8. linux系统共享文件夹,Linux系统如何设置共享文件夹?新建文件夹以及指令是什么?...
  9. Keil uVision5 MDK-ARM 程序使用—————中文图解
  10. android 蓝牙连接苹果手机号码,苹果与android蓝牙连接怎么实现
  11. php获取alexa世界排名值的函数
  12. iOS 横线贯穿文字
  13. Linux系统安装--LInix系统随笔(二)
  14. 升级 MDK 5.37 后的问题处理: AC6编译选项, printf, 重启失效等
  15. word里的表格出现换页表格不会自动跳到下一页(已解决)
  16. linux基础知识全面总结,51CTO博客-专业IT技术博客创作平台-技术成就梦想
  17. 淘系技术内容中台负责人接受采访回应“淘宝为什么要做媒体化升级”
  18. mount point / 挂载点
  19. Jmeter接口测试-if控制器
  20. 面试经验之:蚂蚁饿了么抖音美团等多家面试问题!简历优化等

热门文章

  1. java程序报错后跳过继续执行_java笔记.基础篇.基本数据类型
  2. python 输入华氏温度f_如何用 python编写华氏摄氏度的相互转换?
  3. git 打tag_团队开发中的 Git 实践
  4. VUE3搞一下数据录入
  5. Hibernate常见面试题
  6. 黑莓桌面管理器4.2_虚拟桌面管理器行业现状调研分析及发展趋势预测报告(2020)...
  7. double float区别 java,float和double有什么区别?
  8. 基于OctTree的快速最近颜色搜索
  9. SQL 使用总结六(改善数据库性能)
  10. mysql jdbc百度_mysql8.0 jdbc连接注意事项