Dart语言之树的遍历

981 阅读2分钟

今晚Jack来说一说Dart语言实现树的遍历。先用源码演示一下树的 前序遍历,中序遍历 和 后序遍历;再谈一谈树的层序遍历的两种方法(广度优先比遍历 和 按层打印);最后演示一下已知前序遍历和中序遍历,如何确定树的后序遍历。

今天的演示全都以下图这棵树为例:

treeTraversal

1.前序遍历:先打印当前节点值,再打印左儿子的值,最后打印右儿子的值;

中序遍历:先打印当前节点左儿子的值,再打印当前节点值,最后打印右儿子的值;

后序遍历:先打印当前节点左儿子的值,再打印右儿子的值,最后打印当前节点的值。

下面上代码,代码评论是遍历的结果:

class TreeNode{
  int val;
  TreeNode leftChild;
  TreeNode rightChild;
  TreeNode(this.val){
      leftChild=null;
      rightChild=null;
  }
}

List<int> preList=List<int>();
List<int> inList=List<int>();
List<int> postList=List<int>();

void preTraversal(TreeNode rt){
  if(rt==null)return;
  preList.add(rt.val);
  preTraversal(rt.leftChild);
  preTraversal(rt.rightChild);
}

void inTraversal(TreeNode rt){
  if(rt==null)return;
  inTraversal(rt.leftChild);
  inList.add(rt.val);
  inTraversal(rt.rightChild);
}

void postTraversal(TreeNode rt){
  if(rt==null)return;
  postTraversal(rt.leftChild);
  postTraversal(rt.rightChild);
  postList.add(rt.val);
}

void main(){
  TreeNode root=TreeNode(5);
  root
      ..leftChild=TreeNode(3)
      ..rightChild=TreeNode(10)
      ..leftChild.leftChild=TreeNode(43)
      ..leftChild.rightChild=TreeNode(21)
      ..leftChild.leftChild.leftChild=TreeNode(12)
      ..rightChild.leftChild=TreeNode(76)
      ..rightChild.rightChild=TreeNode(9)
      ..rightChild.leftChild.leftChild=TreeNode(45)
      ..rightChild.leftChild.rightChild=TreeNode(8);
    
    preTraversal(root);
    print('The preorder list is $preList');

    inTraversal(root);
    print('The inorder list is $inList');

    postTraversal(root);
    print('The postorder list is $postList');
}
/*
Result of three traversals:
The preorder list is [5, 3, 43, 12, 21, 10, 76, 45, 8, 9]
The inorder list is [12, 43, 3, 21, 5, 45, 76, 8, 10, 9]
The postorder list is [12, 43, 21, 3, 45, 8, 76, 9, 10, 5]
*/

2.层序遍历:就是根据节点深度从小到大,从左到右打印一棵树的所有节点值。

先给一下 广度优先遍历(breadth first traversal)的代码,用queue来实现:

import 'dart:collection';

class TreeNode{
  int val;
  TreeNode leftChild;
  TreeNode rightChild;
  TreeNode(this.val){
      leftChild=null;
      rightChild=null;
  }
}

List<int> levelOrder=List<int>();

void bfs(TreeNode rt){
  if(rt==null)return;
  Queue<TreeNode> qt=Queue<TreeNode>();
  qt.add(rt);
  while(qt.isNotEmpty){
    TreeNode temp=qt.removeFirst();
    levelOrder.add(temp.val);
    if(temp.leftChild!=null)qt.add(temp.leftChild);
    if(temp.rightChild!=null)qt.add(temp.rightChild);
  }
  print('The result of level order traversal is $levelOrder');
}

void main(){
  TreeNode root=TreeNode(5);
  root
      ..leftChild=TreeNode(3)
      ..rightChild=TreeNode(10)
      ..leftChild.leftChild=TreeNode(43)
      ..leftChild.rightChild=TreeNode(21)
      ..leftChild.leftChild.leftChild=TreeNode(12)
      ..rightChild.leftChild=TreeNode(76)
      ..rightChild.rightChild=TreeNode(9)
      ..rightChild.leftChild.leftChild=TreeNode(45)
      ..rightChild.leftChild.rightChild=TreeNode(8);

  bfs(root);
}
/*
Result of breadth first traversal:
The result of level order traversal is [5, 3, 10, 43, 21, 76, 9, 12, 45, 8]
*/

再给一下按层打印的源码,这里主要是用到了递归思想,无论是取树高度还是打印指定层的所有节点:

import 'dart:math';

class TreeNode{
  int val;
  TreeNode leftChild;
  TreeNode rightChild;
  TreeNode(this.val){
      leftChild=null;
      rightChild=null;
  }
}

List<int> levelOrder=List<int>();

int getHeight(TreeNode root){
  if(root==null)return 0;
  return max(getHeight(root.leftChild), getHeight(root.rightChild))+1;
}

void printGivenLevel(TreeNode root, int level){
  if(root==null)return;
  if(level>1){
    printGivenLevel(root.leftChild, level-1);
    printGivenLevel(root.rightChild, level-1);
  }else levelOrder.add(root.val);
}

void printAllLevels(TreeNode root){
  int treeHeight=getHeight(root);
  for(int i=1;i<=treeHeight;i++)
  printGivenLevel(root, i);
  print('The result of level order traversal is $levelOrder');
}

void main(){
  TreeNode root=TreeNode(5);
  root
      ..leftChild=TreeNode(3)
      ..rightChild=TreeNode(10)
      ..leftChild.leftChild=TreeNode(43)
      ..leftChild.rightChild=TreeNode(21)
      ..leftChild.leftChild.leftChild=TreeNode(12)
      ..rightChild.leftChild=TreeNode(76)
      ..rightChild.rightChild=TreeNode(9)
      ..rightChild.leftChild.leftChild=TreeNode(45)
      ..rightChild.leftChild.rightChild=TreeNode(8);

  printAllLevels(root);
}
/*
Result of printAllLevels method:
The result of level order traversal is [5, 3, 10, 43, 21, 76, 9, 12, 45, 8]
*/

3.最后解一道题,已知前序遍历和中序遍历,求一棵树的后序遍历。

其实也可以是 已知中序遍历和后序遍历,求一棵树的中序遍历。

但是 已知前序遍历和后序遍历,却不能唯一确定一棵树,那么也就不能唯一确定它的中序遍历。原因见下图,大家体会一下:

why not unique

最后贴一下 前序遍历和中序遍历确定后序遍历的Dart源码,代码评论是结果:

class TreeNode{
  int val;
  TreeNode leftChild;
  TreeNode rightChild;
  TreeNode(this.val){
      leftChild=null;
      rightChild=null;
  }
}

List<int> preorder=[5, 3, 43, 12, 21, 10, 76, 45, 8, 9];
List<int> inorder=[12, 43, 3, 21, 5, 45, 76, 8, 10, 9];
List<int> postList=List<int>();

TreeNode getRoot(int prel,int prer,int inl,int inr){
  int prell,prelr,prerl,prerr,inll,inlr,inrl,inrr;
  TreeNode root=TreeNode(preorder[prel]);
  int rtPos=inorder.indexOf(preorder[prel]);
  if(rtPos!=inl){
      prell=prel+1;
      inll=inl;
      inlr=rtPos-1;
      prelr=prell+inlr-inll;
      root.leftChild=getRoot(prell, prelr, inll, inlr);
  } 
  if(rtPos!=inr){
      prerr=prer;
      inrl=rtPos+1;
      inrr=inr;
      prerl=prer-(inrr-inrl);
      root.rightChild=getRoot(prerl,prerr,inrl,inrr);
  }
  return root;
}

void postTraversal(TreeNode rt){
  if(rt==null)return;
  postTraversal(rt.leftChild);
  postTraversal(rt.rightChild);
  postList.add(rt.val);
}

void main(){
  TreeNode root=getRoot(0, 9, 0, 9);
  postTraversal(root);
  print('The result of postorder traversal is $postList');
}


/*
The result of postorder traversal is [12, 43, 21, 3, 45, 8, 76, 9, 10, 5]
*/