题目:
在这一部分对于二叉树的遍历进行总结,主要包括以下几个方面:
(1)从上到下打印二叉树
(2)二叉树的深度优先搜索(前,中,后的递归与非递归实现)
(3)二叉树的广度优先搜索
(4)分行从上到下打印二叉树
(5)之字形打印二叉树(锯齿形打印二叉树103leetcode)
程序链接:https://blog.csdn.net/u013132035/article/details/80604718
https://blog.csdn.net/GSCurry/article/details/77993483
https://blog.csdn.net/huyang0304/article/details/82389595
思路:二叉树是一种非常重要的数据结构,非常多其他数据结构都是基于二叉树的基础演变而来的。对于二叉树,有深度遍历和广度遍历,深度遍历有前序、中序以及后序三种遍历方法,广度遍历即我们寻常所说的层次遍历。由于树的定义本身就是递归定义,因此採用递归的方法去实现树的三种遍历不仅容易理解并且代码非常简洁,而对于广度遍历来说,须要其他数据结构的支撑。比方堆了。所以。对于一段代码来说,可读性有时候要比代码本身的效率要重要的多。
一 .从上到下打印二叉树(不分行)
这个好像就是二叉树的层次遍历,是广度优先搜索
每次打印一个节点的时候,如果该节点有子节点,则把该子节点放到一个队列的末尾。接下来到队列的头部取出最早进入队列的节点重复前面的打印操作,直至对列中所有的节点都被打印出来。
LinkedList中有一些方法有点不熟,总结在下面:
add(E e):在链表后添加一个元素; 通用方法
addFirst(E e):在链表头部插入一个元素; 特有方法
addLast(E e):在链表尾部添加一个元素; 特有方法
push(E e):与addFirst方法一致
offer(E e):在链表尾部插入一个元素
pop():和removeFirst方法一致,删除头。
poll():查询并移除第一个元素 特有方法
代码:
(1)public ArrayList<Integer> printFromTopToBottom(BinaryTreeNode pTreeRoot){ //非递归版本
ArrayList<Integer> list = new ArrayList<Integer>();
if(pTreeRoot == null){
return null;
}
Queue<BinaryTreeNode> queue = new LinkedList<BinaryTreeNode>();
queue.offer(pTreeRoot); //在链表末尾添加一个元素
while(!queue.isEmpty()){
BinaryTreeNode treeNode = queue.poll();
if(treeNode.left!=null){
queue.offer(treeNode.left);
}
if(treeNode.right!=null){
queue.offer(treeNode.right);
}
list.add(treeNode.data);
}
return list;
}
//层序遍历二叉树递归实现
递归版本的思想就是先找出这个二叉树中有多少层,然后在一层一层打印出来。
private int depth(BinaryTreeNode pTreeRoot){
if(pTreeRoot==null){
return 0;
}
int l = depth(pTreeRoot.left);
int r = depth(pTreeRoot.right);
if(l > r){
return l + 1;
}else{
return r + 1;
}
}
private void levelOrder(BinaryTreeNode pTreeNode, int level) {
if(pTreeNode == null || level < 1){
return ;
}
if(level == 1){
System.out.print(pTreeNode.data+ " ");
return ;
}
//左子树
levelOrder(pTreeNode.left, level-1);
//右子树
levelOrder(pTreeNode.right, level-1);
}
public void printFromTopToBottom(BinaryTreeNode pTreeRoot){
if(pTreeRoot==null){
return ;
}
int depth = depth(pTreeRoot);
for (int i = 1; i <= depth; ++i) {
levelOrder(pTreeRoot, i);
}
}
二.二叉树的深度遍历
1.递归版深度遍历
树的深度优先遍历:对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次。如果你还不能理解的话,其实我们常用的二叉树先序遍历(根左右)、中序遍历(左根右)、后序遍历(左右根)就是深度遍历。因二叉树比较特殊,故它的深度遍历就前面三种。
用递归的方式实现比较简单,这三种递归方式是函数的调用,加入集合中的顺序不同。
//前序遍历二叉树
ArrayList<Integer> list = new ArrayList<Integer>();
public ArrayList<Integer> preOrderBinaryTree(BinaryTreeNode pTreeRoot){
if(pTreeRoot!=null){
list.add(pTreeRoot.data); //先根结点
preOrderBinaryTree(pTreeRoot.left); //再左边
preOrderBinaryTree(pTreeRoot.right); //再右边
}else{
return null;
}
return list;
}
//中序遍历二叉树
public ArrayList<Integer> inOrderBinaryTree(BinaryTreeNode pTreeRoot){
if(pTreeRoot!=null){
inOrderBinaryTree(pTreeRoot.left); //先左边
list.add(pTreeRoot.data); //再中间
inOrderBinaryTree(pTreeRoot.right); //再右边
}else{
return null;
}
return list;
}
//后序遍历二叉树
public ArrayList<Integer> postOrderBinaryTree(BinaryTreeNode pTreeNode){
if(pTreeNode!=null){
postOrderBinaryTree(pTreeNode.left); //先左边
postOrderBinaryTree(pTreeNode.right); //再右边
list.add(pTreeNode.data); //再中间
}else{
return null;
}
return list;
}
2.非递归版深度遍历
非递归的深度遍历其实就是使用栈。对于前序和中序遍历就是最后添加到结果的顺序不同。
我自己写过这个代码,出现的问题是一开始栈就是空的,是不用先在栈中添加根节点的。
public void preOrderBinaryTree(BinaryTreeNode pTreeNode) { //前序
Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
while(pTreeNode!=null || !stack.isEmpty()){ //当前节点不为空或者栈中不为空
while(pTreeNode!=null){
System.out.print(pTreeNode.data + " ");//这部分就相当于是输出了,或者是将数值添加到最后的结果中
stack.push(pTreeNode); //将当前节点放入栈中
pTreeNode = pTreeNode.left; //处理左节点
}
if(!stack.isEmpty()){ //当处理完左结点之后
pTreeNode = stack.pop(); //处理当前节点的右节点
pTreeNode = pTreeNode.right;
}
}
}
public void inOrderBinaryTree(BinaryTreeNode pTreeNode){ //中序
Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
while(pTreeNode!=null || !stack.isEmpty()){
while(pTreeNode!=null){ //先处理左结点这一路
stack.push(pTreeNode);
pTreeNode = pTreeNode.left;
}
if(!stack.isEmpty()){
pTreeNode = stack.pop();
System.out.print(pTreeNode.data + " ");//在这一步的时候输出值或者添加
pTreeNode = pTreeNode.right;
}
}
}
后序遍历的部分在leetcode145.
三.二叉树的广度优先搜搜
public void levelOrderTraversal(LsitNode node){ //二叉树的广度优先搜索非递归
if(node==null){
System.out.print("empty tree");
return;
}
ArrayDeque<ListNode> deque = new ArrayDeque<ListNode>();
deque.add(node);
while(!deque.isEmpty()){
ListNode rnode = deque.remove();
System.out.print(rnode.val+" ");
if(rnode.left!=null){
deque.add(rnode.left);
}
if(rnode.right!=null){
deque.add(rnode.right);
}
}
}
四.分行从上到下打印二叉树
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) { //分行从上到下打印二叉树
ArrayList<ArrayList<Integer>> res = new ArrayList<>();//用来存放最终的结果
if (pRoot == null)
return res;
LinkedList<TreeNode> queue = new LinkedList<>(); //广度优先搜索用到的队列
queue.add(pRoot);
ArrayList<Integer> list = new ArrayList<>(); //暂时存放每一行的节点
int start = 0; //当前行已经访问的元素个数
int end = 1; //当前行一共的元素个数
while (!queue.isEmpty()) {
TreeNode node = queue.pop();
list.add(node.val);
start++;
if (node.left != null)
queue.offer(node.left);
if (node.right != null)
queue.offer(node.right);
if (start == end) { //当当前行已经取完时
start = 0;
end = queue.size();
res.add(new ArrayList<>(list)); //把list存放在结果中
list.clear(); //list清空
}
}
return res;
}
五.之字打印二叉树
分层分行打印二叉树,只不过顺序是锯齿形的
需要时会用两个栈,然后有一个类似标志位的level表明使用的是哪一个栈
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) { //之字打印二叉树
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
if (pRoot == null)
return res;
Stack<TreeNode> s1 = new Stack<>(); // s1表示奇数,从左往右输出
Stack<TreeNode> s2 = new Stack<>(); // s2表示偶数,从右往左输出
s1.push(pRoot);
int level = 1;
while (!s1.empty() || !s2.empty()) {
if (level % 2 != 0) { //当前层为奇数,从s1中取数据,将下层数据存放在s2
ArrayList<Integer> list = new ArrayList<>();
while (!s1.empty()) {
TreeNode node = s1.pop();
if (node != null) {
list.add(node.val);
s2.push(node.left);
s2.push(node.right);
}
}
if (!list.isEmpty()) {
res.add(list);
level++;
}
} else {
ArrayList<Integer> list = new ArrayList<>(); //当前层为偶数,将下层数据存放在s1
while (!s2.empty()) {
TreeNode node = s2.pop();
if (node != null) {
list.add(node.val);
s1.push(node.right);
s1.push(node.left);
}
}
if (!list.isEmpty()) {
res.add(list);
level++;
}
}
}
return res;
}