您的位置:首页 > 理论基础 > 数据结构算法

算法与数据结构-二叉树 讲解与java代码实现

2018-01-06 16:51 1031 查看

1. 三种遍历方式和宽度优先遍历



递归实现先序,中序,后序遍历

import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}*/
public class TreeToSequence {
public int[][] convert(TreeNode root) {
// write code here

ArrayList<Integer> temp1=new ArrayList<Integer>();
ArrayList<Integer> temp2=new ArrayList<Integer>();
ArrayList<Integer> temp3=new ArrayList<Integer>();

pre(root,temp1);
mid(root,temp2);
post(root,temp3);
int[][] result=new int[3][temp1.size()];
for(int i=0;i<temp1.size();i++){
result[0][i]=temp1.get(i);
result[1][i]=temp2.get(i);
result[2][i]=temp3.get(i);
}

return result;
}

public void pre(TreeNode root,ArrayList<Integer> result){
if(root==null){
return;
}

result.add(root.val);
pre(root.left,result);
pre(root.right,result);
}

public void mid(TreeNode root,ArrayList<Integer> result){
if(root==null){
return;
}

mid(root.left,result);
result.add(root.val);
mid(root.right,result);
}

public void post(TreeNode root,ArrayList<Integer> result){
if(root==null){
return;

}
post(root.left,result);
post(root.right,result);
result.add(root.val);
}
}


非递归实现先序,中序,后序遍历









import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}*/
public class TreeToSequence {
public int[][] convert(TreeNode root) {
// write code here

ArrayList<Integer> preRes=pre(root);
ArrayList<Integer> midRes=mid(root);
//ArrayList<Integer> postRes=post(root);
ArrayList<Integer> postRes=post_oneStack(root);

int[][] result=new int[3][preRes.size()];

for(int i=0;i<preRes.size();i++){
result[0][i]=preRes.get(i);
result[1][i]=midRes.get(i);
result[2][i]=postRes.get(i);
}

return result;
}
//非递归的先序遍历
public ArrayList<Integer> pre(TreeNode root){
Stack<TreeNode> stack=new Stack<TreeNode>();
ArrayList<Integer> result=new ArrayList<Integer>();
stack.push(root);
TreeNode cur;
while(!stack.isEmpty()){
cur=stack.pop();
result.add(cur.val);
if (cur.right!=null){
stack.push(cur.right);
}
if(cur.left!=null){
stack.push(cur.left);
}
}
return result;
}

public ArrayList<Integer> mid(TreeNode root){
Stack<TreeNode> stack=new Stack<TreeNode>();
ArrayList<Integer> result=new ArrayList<Integer>();
TreeNode cur;
cur=root;
while(cur!=null||!stack.isEmpty()){
//将左子树依次压入栈
while(cur!=null){
stack.push(cur);
cur=cur.left;
}
//当左子树全部压入栈后,开始从栈顶取元素并指定cur等于右子树
cur=stack.pop();
result.add(cur.val);
cur=cur.right;
}
return result;
}

public ArrayList<Integer> post(TreeNode root){
Stack<TreeNode> stack1=new Stack<TreeNode>();
Stack<TreeNode> stack2=new Stack<TreeNode>();
ArrayList<Integer> result=new ArrayList<Integer>();
TreeNode cur;
stack1.push(root);
while(!stack1.isEmpty()){
cur=stack1.pop();
stack2.push(cur);
if(cur.left!=null){
stack1.push(cur.left);
}
if(cur.right!=null){
stack1.push(cur.right);
}
}

while(!stack2.isEmpty()){
cur=stack2.pop();
result.add(cur.val);
}

return result;

}

public ArrayList<Integer> post_oneStack(TreeNode root){
Stack<TreeNode> stack=new Stack<TreeNode>();
stack.push(root);
ArrayList<Integer> result=new ArrayList<Integer>();
TreeNode lastNode=root,cur=null;//分别指向上次打印的节点以及当前节点

while(!stack.isEmpty()){
cur=stack.peek();
if(cur.left!=null&&cur.left!=lastNode&&cur.right!=lastNode){
stack.push(cur.left);
}else if(cur.right!=null&&cur.right!=lastNode){
stack.push(cur.right);
}else{
cur=stack.pop();
result.add(cur.val);
lastNode=cur;
}
}

return result;

}
}


宽度优先遍历(按层从左到右遍历)



import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}*/
public class TreePrinter {
public int[][] printTree(TreeNode root) {
//保存每一行的值
ArrayList<Integer> temp=new ArrayList<Integer>();
//保存总的结果的list
ArrayList<ArrayList<Integer>> resList=new ArrayList<ArrayList<Integer>>();
Queue<TreeNode> queue=new LinkedList<TreeNode>();//队列,因为是按层从左到右遍历,先进先出
queue.offer(root);
TreeNode cur=root,last=root,nlast=null;//分别是当前节点,本层最后一个节点,下一层最后一个节点
while(!queue.isEmpty()){
cur=queue.poll();
temp.add(cur.val);
if(cur.left!=null){
queue.offer(cur.left);
nlast=cur.left;
}
if(cur.right!=null){
queue.offer(cur.right);
nlast=cur.right;
}
//如果当前节点为本层最后一个节点,则要把现在的节点加入结果,并令last等于nlast
if(cur==last){
resList.add(temp);
temp=new ArrayList<Integer>();//不能使用Clear清空,否则存进入的就变成了空,因为保存的是引用
last=nlast;
}
}
int layers=resList.size();
int[][] result=new int[layers][];
for(int i=0;i<layers;i++){
result[i]=new int[resList.get(i).size()];
for(int j=0;j<result[i].length;j++){
result[i][j]=resList.get(i).get(j);
}
}

return result;

}
}


二叉树的序列化打印:先序遍历

import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}*/
public class TreeToString {
public String toString(TreeNode root) {
// write code here
Stack<TreeNode> stack=new Stack<TreeNode>();
String result=new String();
stack.push(root);
TreeNode cur;
while(!stack.isEmpty()){
cur=stack.pop();
if(cur==null){
result+="#!";
}else{
result+=cur.val+"!";
stack.push(cur.right);
stack.push(cur.left);
}
}
return result;
}
}


纸张对折n次,求折痕方向:使用中序逆序遍历,此处的逆序可以省去,因为是题目规定哪个方向为正



折纸方向打印 实现

import java.util.*;
public class FoldPaper {
public String[] foldPaper(int n) {
// write code here
ArrayList<String> temp=new ArrayList<String>();
//dir false表示"down",true表示"up"
printDir(n,false,temp);
int nodeSize=temp.size();
String[] result=new String[nodeSize];
for(int i=0;i<nodeSize;i++){
result[i]=temp.get(i);
}
return result;
}
//中序逆序遍历递归打印
public void printDir(int n,boolean dir,ArrayList<String> result){
if(n<1){
return;
}
printDir(n-1,false,result);
result.add(dir?"up":"down");
printDir(n-1,true,result);
}
}


树上最远距离







import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}*/
public class LongestDistance {
public int findLongest(TreeNode root) {

int max=getDistance(root,0)[1];
return max;
}

//节点只能经过一次,那么当前节点的左右两边最远距离是最可能为最大距离的,每次要计算这个值
//同时需要返回距离头结点的单边最远距离为后续计算提供依据
//第一个返回参数为当前头结点的单边最远距离,第二个返回的参数为当前已经求得的最远距离
public int[] getDistance(TreeNode root,int max){
int[] result=new int[2];
result[1]=max;
if(root==null){
result[0]=0;
return result;
}
int[] leftRes=getDistance(root.left,max);
int[] rightRes=getDistance(root.right,max);
int lDistToRoot=leftRes[0];
int rDistToRoot=rightRes[0];
int twoSideDist=lDistToRoot+rDistToRoot+1;
max=Math.max(leftRes[1],rightRes[1]);
if(twoSideDist>max){
max=twoSideDist;
}
result[0]=Math.max(lDistToRoot,rDistToRoot)+1;
result[1]=max;
return result;

}
}


2.三种树及其判定方法

二叉树的子树定义



平衡二叉树的定义



平衡二叉树的递归判定

import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}*/
public class CheckBalance {
public boolean check(TreeNode root) {
// write code here
return getHeight(root)>=0;
}
//递归 求以root为头结点的树的最大高度
public int getHeight(TreeNode root){
//如果是空节点或者空树,则高度为0
if(root==null){
return 0;
}
int left=getHeight(root.left);
int right=getHeight(root.right);
//如果左子树或者右子树不平衡,则返回-1
if(left<0||right<0){
return -1;
}
//如果左子树或者右子树高度差大于1,则返回-1
if(Math.abs(left-right)>1){
return -1;
}
//返回左子树和右子树中最大的高度作为本棵树的高度
return left>right?left+1:right+1;
}
}


搜索二叉树



最大搜索子树







最大搜索二叉子树递归判定

import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}*/
public class MaxSubtree {
public int nodeNum=0;//以当前节点为根节点的树的节点数
public int MAX=Integer.MAX_VALUE;
public int MIN=Integer.MIN_VALUE;
public int max,min;//以当前节点为根节点的树的最大最小值

public TreeNode getMax(TreeNode root) {
return getInfo(root);
}

public TreeNode getInfo(TreeNode root){
int lNodeNum=0,rNodeNum=0;//左子树,右子树的节点数
int lMax,lMin;//左子树的最大节点值,最小节点值
int rMax,rMin;
TreeNode lSearchRoot,rSearchRoot;//左右子树如果是二叉搜索树,则保存他们的头结点引用,若为null,则不是二叉搜索树
if(root==null){
nodeNum=0;
max=MIN;
min=MAX;
return null;
}

lSearchRoot=getInfo(root.left);
lNodeNum=nodeNum;
lMax=max;lMin=min;

rSearchRoot=getInfo(root.right);
rNodeNum=nodeNum;
rMax=max;rMin=min;

min=Math.min(root.val,lMin);
max=Math.max(root.val,rMax);
nodeNum=Math.max(lNodeNum,rNodeNum);
//当前树为二叉搜索树,要求左子树和右子树均为二叉搜索树,且以当前节点为头结点的树为二叉搜索树
//此处子树的二叉搜索树判定不能写成lSearchRoot!=null,因为要考虑到有叶节点的情况
if(lSearchRoot==root.left&&rSearchRoot==root.right&&lMax<root.val&&root.val<rMin){
nodeNum=lNodeNum+rNodeNum+1;
return root;
}
if(lNodeNum==rNodeNum){
return lSearchRoot.val>rSearchRoot.val?lSearchRoot:rSearchRoot;
}
return lNodeNum>rNodeNum?lSearchRoot:rSearchRoot;
}
}


搜索二叉树寻找位置错误的两个节点



import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}*/
public class FindErrorNode {
public int[] findError(TreeNode root) {
// write code here
ArrayList<Integer> result=new ArrayList<Integer>();//保存数值的list
int[] temp=new int[2];
boolean descendTime=false;
//是否曾经遇到降序:因为错位的只有2个节点,分为两种情况
//如果此时为第一次,则降序的前面个数为错位大的数,后面的数暂时存为错位小的数
//如果此时为第二次降序,则降序的后面的数为错位小的数
mid(root,result);
for(int i=1;i<result.size();i++){
if(result.get(i)<result.get(i-1)){
temp[0]=result.get(i);
if(descendTime==false){
descendTime=true;
temp[1]=result.get(i-1);
}
}

}
return temp;
}

public void mid(TreeNode root,ArrayList<Integer> result){
if(root==null){
return;
}

mid(root.left,result);
result.add(root.val);
mid(root.right,result);
}
}


满二叉树







完全二叉树的判断



完全二叉树的判定:使用队列进行宽度优先遍历

import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}*/
public class CheckCompletion {
public boolean chk(TreeNode root) {
// write code here
Queue<TreeNode> queue=new LinkedList<TreeNode>();
queue.offer(root);
TreeNode cur;
boolean hasToBeLeaf=false;
while(!queue.isEmpty()){
cur=queue.poll();
//如果当前节点的左孩子或者右孩子有一个为空
if(cur.left==null||cur.right==null){
//如果当前节点有右孩子却没有左孩子,则返回false,因为违背了缺少的节点全部集中在右边这一条
if(cur.left==null&&cur.right!=null) return false;
//因为缺少的节点需要全部集中在右边,若当前节点只有左孩子却没有右孩子,则后继节点必须为叶节点;
if(hasToBeLeaf==false) hasToBeLeaf=true;
//若此节点必须为叶节点却有左孩子,则返回false
else if(cur.left!=null){
return false;
}
}else{
queue.offer(cur.left);
queue.offer(cur.right);
}

}
return true;
}
}


注释和引用:

java实现:

* Stack Queue

* Stack<TreeNode> stack=new Stack<TreeNode>();//声明和定义
* push() pop()//入栈和出栈
* peek() //方法调用返回在这个堆栈的顶部的对象,但是不弹出
* Queue<TreeNode> queue=new LinkedList<TreeNode>();//队列的声明和定义
* offer() poll()//入队和出队
* isEmpty() //判断栈和队列是否为空
* http://www.runoob.com/java/data-queue.html 队列 菜鸟教程
* 数组的大小用length属性,list的大小用size()函数

* Arraylist<type(类名比如Integer或者String)>
* List<String> temp=new ArrayList<String>();//初始化动态链表
* size()//List或者ArrayList的元素个数
* add()//添加元素
* get(i)获取元素


牛客网算法课程视频链接
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: