您当前的位置: 首页 >  数据结构与算法

命运之手

暂无认证

  • 4浏览

    0关注

    747博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【数据结构与算法】【11】树的遍历算法

命运之手 发布时间:2022-06-03 16:07:34 ,浏览量:4

为什么要学习树的遍历算法

二叉树的遍历方式有多种,根据每种遍历方式的不同特性,在现实中有着不同的应用价值

随着我们学得越深入越底层,就会发现二叉树的应用无处不在,很多基础技术,底层都是通过二叉树实现的

比如数据库系统、文件系统、编译系统、组织架构展示等等

我们先掌握每种遍历算法,以后再学习其具体应用时,就很容易理解了

广度优先遍历和深度优先遍历

二叉树遍历方式整体分为两大类

  • 广度优先遍历,Breadth First Search,简称BFS遍历 这种方式先访问父节点,再访问父节点所有的直接子节点,再按此方式,对每个直接子节点下面的子树进行递归遍历
  • 深度优先遍历,Depth First Search,简称DFS遍历 深度遍历分为好几种方式,它们的共同特征是,先遍历完一个节点下面的整个子树,再遍历下个同级节点的子树
  • 广度遍历和深度遍历的区别,可以简单概括为,广度遍历是先遍历同级节点,深度遍历是先遍历当前节点子树

在这里插入图片描述 在这里插入图片描述 深度优先遍历的几种方式

上面已经提到,深度优先遍历是先遍历当前子树,再遍历同级节点

而子树如何遍历,根据父节点的访问顺序,可分为三种方式

  • 先序遍历,Preorder Traversal 按父节点-左子树-右子树的顺序,遍历每个节点所在的树
  • 中序遍历,Inorder Traversal 按左子树-父节点-右子树的顺序,遍历每个节点所在的树
  • 后序遍历,Postorder Traversal 按左子树-右子树-父节点的顺序,遍历每个节点所在的树

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 定义树和测试数据

我们先构建这样的一颗树,作为测试数据,然后再分别以不同的遍历算法,来遍历该树

在这里插入图片描述


	import java.util.LinkedList;
	import java.util.List;
	import java.util.Objects;
	
	@SuppressWarnings("all")
	public class TreeNode {
	
	    //树深度
	    public Integer depth = 0;
	
	    //数值
	    public T data = null;
	
	    //添加transient关键字,是告诉JSON等序列化功能,在序列化时忽略此字段
	    //父节点包含子节点,字节点也包含父节点,对象之间相互循环引用,会导致序列化时产生死循环
	    //可以通过transient来忽略parentNode字段,通过parentId来查找
	    public transient TreeNode parent;
	    public final LinkedList children = new LinkedList();
	
	    //添加子节点
	    public void add(TreeNode child) {
	        child.parent = this;
	        child.depth = depth + 1;
	        children.addLast(child);
	    }
	
	    //移除子节点
	    public void remove(TreeNode child) {
	        child.parent = null;
	        child.depth = null;
	        children.remove(child);
	    }
	
	    //添加子节点
	    public void add(T data) {
	        TreeNode child = new TreeNode();
	        child.data = data;
	        child.parent = this;
	        child.depth = depth + 1;
	        children.addLast(child);
	    }
	
	    //移除子节点
	    public void remove(T data) {
	        List deleteNodes = new LinkedList();
	        for (TreeNode child : children)
	            if (Objects.equals(child.data, data)) {
	                child.parent = null;
	                child.depth = null;
	                deleteNodes.add(child);
	            }
	        children.removeAll(deleteNodes);
	    }
	
	    //打印当前子树
	    @Override
	    public String toString() {
	        String childDescription = "";
	        if (!children.isEmpty()) {
	            childDescription = childDescription + "(";
	            for (TreeNode child : children)
	                childDescription = childDescription + child.data;
	            childDescription = childDescription + ")";
	        }
	        return data + childDescription;
	    }
	}


	import java.util.List;
	
	//遍历parentNode,将其所有节点按遍历顺序存到resultList当中
	public interface ITreeTraverser {
	
	    void traverse(TreeNode parentNode, List resultList);
	}


	import java.util.LinkedList;
	import java.util.List;
	
	public class Demo {
	
	    public static TreeNode createTree() {
	        TreeNode a = new TreeNode();
	        a.data = "A";
	        TreeNode b = new TreeNode();
	        b.data = "B";
	        TreeNode c = new TreeNode();
	        c.data = "C";
	        TreeNode d = new TreeNode();
	        d.data = "D";
	        TreeNode e = new TreeNode();
	        e.data = "E";
	        TreeNode f = new TreeNode();
	        f.data = "F";
	        TreeNode g = new TreeNode();
	        g.data = "G";
	        a.add(b);
	        a.add(c);
	        b.add(d);
	        b.add(e);
	        c.add(f);
	        c.add(g);
	        return a;
	    }
	
	    public static void main(String[] args) {
	        TreeNode tree = createTree();
	        List list = new LinkedList();
	        ITreeTraverser traverser = new XXXTraverserImpl();
	        traverser.traverse(tree, list);
	        for (TreeNode node : list)
	            System.out.println(node);
	    }
	}

广度优先遍历代码实现


	import java.util.LinkedList;
	import java.util.List;
	
	public class BreadthTraversal implements ITreeTraverser {
	
	    public void traverse(TreeNode parentNode, List resultList) {
	        List levelNodeList = new LinkedList();
	        levelNodeList.add(parentNode);
	        traverse(levelNodeList, resultList);
	    }
	
	    //当前层次的所有节点
	    private void traverse(List levelNodeList, List resultList) {
	        if (levelNodeList.isEmpty())
	            return;
	        resultList.addAll(levelNodeList);
	        List nextLevelNodeList = new LinkedList();
	        for (TreeNode node : levelNodeList)
	            nextLevelNodeList.addAll(node.children);
	        traverse(nextLevelNodeList, resultList);
	    }
	}

打印结果

A(BC) B(DE) C(FG) D E F G

先序遍历代码实现


	import java.util.List;
	
	public class PreorderTraversal implements ITreeTraverser {
	
	    public void traverse(TreeNode parentNode, List resultList) {
	        resultList.add(parentNode);
	        if (parentNode.children.size() >= 1) {
	            TreeNode leftChild = parentNode.children.get(0);
	            traverse(leftChild, resultList);
	        }
	        if (parentNode.children.size() >= 2) {
	            TreeNode rightChild = parentNode.children.get(1);
	            traverse(rightChild, resultList);
	        }
	    }
	}

打印结果

A(BC) B(DE) D E C(FG) F G

中序遍历代码实现


	import java.util.List;
	
	public class InorderTraversal implements ITreeTraverser {
	
	    public void traverse(TreeNode parentNode, List resultList) {
	        if (parentNode.children.size() >= 1) {
	            TreeNode leftChild = parentNode.children.get(0);
	            traverse(leftChild, resultList);
	        }
	        resultList.add(parentNode);
	        if (parentNode.children.size() >= 2) {
	            TreeNode rightChild = parentNode.children.get(1);
	            traverse(rightChild, resultList);
	        }
	    }
	}

打印结果

D B(DE) E A(BC) F C(FG) G

后序遍历代码实现


	import java.util.List;
	
	public class PostorderTraversal implements ITreeTraverser {
	
	    public void traverse(TreeNode parentNode, List resultList) {
	        if (parentNode.children.size() >= 1) {
	            TreeNode leftChild = parentNode.children.get(0);
	            traverse(leftChild, resultList);
	        }
	        if (parentNode.children.size() >= 2) {
	            TreeNode rightChild = parentNode.children.get(1);
	            traverse(rightChild, resultList);
	        }
	        resultList.add(parentNode);
	    }
	}

打印结果

D E B(DE) F G C(FG) A(BC)

遍历算法的基本应用

  • 广度优先遍历:如果我们想遍历整个树,找到每一层的的最大值,这种方法是最简单直观的 比如大家开年会的时候,想要在每个级别分别选出若干个优秀员工,就可以采用这种算法
  • 深度优先遍历:可用来解析数学公式或代码语句 比如我们要表达出a+b这种运算,就可以通过树来实现,a作为左节点,b作为右节点,+为父节点 这样+所在的子树,就可以代表a+b的运算结果,然后再与其它子树,继续进行运算,就实现了一个数学公式解释器

下一章,我们就要专门来讲解,如何通过树来存储和计算表达式

关注
打赏
1654938663
查看更多评论
立即登录/注册

微信扫码登录

0.1915s