今天复习了二叉树的相关操作,整理归纳如下。
二叉树结点定义
//节点类
private static class TreeNode{
private int val = 0;
private TreeNode left;
private TreeNode right;
public TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
public int getVal() {
return val;
}
public void setVal(int val) {
this.val = val;
}
public TreeNode getLeft() {
return left;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public TreeNode getRight() {
return right;
}
public void setRight(TreeNode right) {
this.right = right;
}
}
二叉树类定义
public class binaryTree {
private TreeNode root;
public binaryTree(){
}
public binaryTree(TreeNode root) {
this.root = root;
}
public TreeNode getRoot() {
return root;
}
public void setRoot(TreeNode root) {
this.root = root;
}
}
前序遍历 递归
//前序遍历 递归
public void preOrderRecursively(TreeNode node){
if(node == null)
return;
System.out.print(node.val+" ");
preOrderRecursively(node.left);
preOrderRecursively(node.right);
}
前序遍历非递归:基本思路是利用栈,从根结点开始,先将根结点进栈,再根结点出栈,然后将根结点的右结点入栈,再左结点入栈。再重复以上循环 栈顶出栈-访问-右子节点入栈-左子节点入栈。
//前序遍历 非递归
public void preOrderNoRecursively(TreeNode node){
Stack<TreeNode> st = new Stack<>();
if(st != null)
st.push(node);
while (st.isEmpty() == false){
TreeNode cur = st.pop();
System.out.print(cur.getVal()+" ");
if(cur.getRight() != null)
st.push(cur.getRight());
if(cur.getLeft() != null)
st.push(cur.getLeft());
}
}
中序遍历递归
//中序遍历 递归
public void inOrderRecursively(TreeNode node){
if(node == null)
return;
inOrderRecursively(node.left);
System.out.print(node.val + " ");
inOrderRecursively(node.right);
}
中序遍历非递归:思路还是利用栈,先从根结点开始走到最左端结点,然后访问该结点,再查看是否存在右子节点,如果有,继续从右子节点朝左走。
//中序遍历 非递归
public void inOrderNoRecursively(TreeNode node){
if(node == null)
return;
Stack<TreeNode> st = new Stack<>();
TreeNode cur = node;
while (cur!=null || st.isEmpty()==false){
while (cur != null){
st.push(cur);
cur = cur.getLeft();
}
if(!st.isEmpty()){
cur = st.pop();
System.out.print(cur.getVal()+" ");
cur = cur.getRight();
}
}
}
后序遍历递归:
//后序遍历 递归
public void postOrderRecursively(TreeNode node){
if(node == null)
return;
postOrderRecursively(node.left);
postOrderRecursively(node.right);
System.out.print(node.getVal()+" ");
}
后序遍历非递归:一个较容易理解的方法是双栈法,即利用两个栈。因为后序遍历的顺序恰好是 先遍历右子树再遍历左子树的前序遍历的逆序
//后序遍历 非递归 可以看作是 先遍历右子树再遍历左子树的前序遍历 的结果 的逆序
public void postOrderNoRecursively(TreeNode node){
if(node == null)
return;
Stack<TreeNode> st = new Stack<>();
Stack<Integer> temp = new Stack<>();
st.push(node);
while (!st.isEmpty()) {
TreeNode cur = st.pop();
temp.push(cur.getVal());
if (cur.getLeft() != null)
st.push(cur.getLeft());
if (cur.getRight() != null)
st.push(cur.getRight());
}
while(!temp.isEmpty()){
System.out.print(temp.pop()+" ");
}
}
层序遍历 并且给出每个结点的深度
利用队列,先将根结点入队,再出队,把根结点的左右子树入队。
//层序遍历 并且给出每个结点的深度
public void levelOrder(TreeNode node){
if(node == null)
return;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(node);
int levelNum = 1;
while (queue.size()!=0){
int len = queue.size();
for (int i = 0; i < len; i++) {
TreeNode temp = queue.poll();
System.out.print(temp.val+" levelNum = "+levelNum+" ");
if(temp.getLeft() != null)
queue.add(temp.getLeft());
if(temp.getRight() != null)
queue.add(temp.getRight());
}
levelNum++;
}
}
层序遍历的应用://求指定结点值的结点深度
//求指定结点值的结点深度
public int levelCount(TreeNode node,int x){
if(node == null)
return -1;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(node);
int levelNum = 1;
while (queue.size()!=0){
int len = queue.size();
for (int i = 0; i < len; i++) {
TreeNode temp = queue.poll();
if(temp.val == x)
return levelNum;
if(temp.getLeft() != null)
queue.add(temp.getLeft());
if(temp.getRight() != null)
queue.add(temp.getRight());
}
levelNum++;
}
return -1;
}
判断两个节点是否为兄弟结点
//判断两个结点是否为兄弟结点 兄弟结点:父亲相同的结点
public boolean isBrother(TreeNode node, int x, int y){//x和y为待判断结点
if(node == null)
return false;
if(node.left != null && node.right != null){
if((node.left.val == x && node.right.val == y) || (node.left.val == y && node.right.val == x)){
return true;
}
}
if(isBrother(node.left,x,y))
return true;
if(isBrother(node.right,x,y))
return true;
return false;
}
判断两个节点是否为堂兄弟结点:堂兄弟结点 深度相同但不是同一个父结点的孩子
//判断两个节点是否为堂兄弟结点:堂兄弟结点 深度相同但不是同一个父结点的孩子
public boolean isCousin(TreeNode node, int x, int y){
if(node == null)
return false;
if(levelCount(node, x) != levelCount(node,y))
return false;
else{
if(isBrother(node, x, y))
return false;
else
return true;
}
}
递归求树的深度
//求树的深度
public int getDepth(TreeNode node){
if(node == null)
return 0;
else {
int hl = getDepth(node.left);
int hr = getDepth(node.right);
return (hr > hr ? hr : hl )+1;
}
}
利用层序遍历求树的宽度
//获取二叉树的宽度
public int getWidth(TreeNode node){
if(node == null)
return 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(node);
int maxWidth = 1;
while (queue.size()!=0){
int len = queue.size();
for (int i = 0; i < len; i++) {
TreeNode temp = queue.poll();
if(temp.getLeft() != null)
queue.add(temp.getLeft());
if(temp.getRight() != null)
queue.add(temp.getRight());
}
if(len > maxWidth)
maxWidth = len;
}
return maxWidth;
}
利用前序和中序序列重建二叉树
例如前序序列{1,2,4,7,3,5,6,8}
中序序列{4,7,2,1,5,3,8,6}
前序序列的第一个数字1是根结点的值,扫描中序序列,位于1前面的{4,7,2}为左子树节点的值,位于1后面的{5.3.8.6}为右子树节点的值,
现在我们对于前半部分 前序{2,4,7}和中序{4,7,2}继续进行以上步骤。
同理采用递归的思路,我们可以完成此题。
//利用前序和中序序列重建二叉树
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if(pre.length == 0 || in.length == 0)
return null;
int len = pre.length-1;
return reConstructBinaryTreeCore(pre,0,len,in,0,len);
}
public TreeNode reConstructBinaryTreeCore(int [] pre, int preStart,int preEnd,int [] in,int inStart,int inEnd){
int rootValue = pre[preStart];
TreeNode root = new TreeNode(rootValue,null,null);
root.left = null;
root.right = null;
if(preStart > preEnd || inStart > inEnd)
return null;
if(preStart == preEnd){
if(inStart == inEnd && pre[preStart]==in[inStart])
return root;
else
return null;
}
int rootInOrder = inStart;
for (int i = inStart; i <= inEnd; i++) {
if(in[i] == rootValue){
rootInOrder = i;
break;
}
}
int leftLength = rootInOrder - inStart ;
int leftPreOrderEnd = preStart + leftLength ;
if(leftLength > 0){
root.left = reConstructBinaryTreeCore(pre,preStart+1,leftPreOrderEnd,in,inStart,rootInOrder-1);
}
if(leftLength < preEnd - preStart){
root.right = reConstructBinaryTreeCore(pre,leftPreOrderEnd+1,preEnd,in,rootInOrder+1,inEnd);
}
return root;
}
完整源程序如下:
/*
* Created by maomao (c)
* 2019. 2. 21.
* at Xi'an jiaotong university.
*/
package DS.binaryTree;
import sun.reflect.generics.tree.Tree;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
public class binaryTree {
private TreeNode root;
public binaryTree(){
}
public binaryTree(TreeNode root) {
this.root = root;
}
public TreeNode getRoot() {
return root;
}
public void setRoot(TreeNode root) {
this.root = root;
}
//节点类
private static class TreeNode{
private int val = 0;
private TreeNode left;
private TreeNode right;
public TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
public int getVal() {
return val;
}
public void setVal(int val) {
this.val = val;
}
public TreeNode getLeft() {
return left;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public TreeNode getRight() {
return right;
}
public void setRight(TreeNode right) {
this.right = right;
}
}
//前序遍历 递归
public void preOrderRecursively(TreeNode node){
if(node == null)
return;
System.out.print(node.val+" ");
preOrderRecursively(node.left);
preOrderRecursively(node.right);
}
//前序遍历 非递归
public void preOrderNoRecursively(TreeNode node){
Stack<TreeNode> st = new Stack<>();
if(st != null)
st.push(node);
while (st.isEmpty() == false){
TreeNode cur = st.pop();
System.out.print(cur.getVal()+" ");
if(cur.getRight() != null)
st.push(cur.getRight());
if(cur.getLeft() != null)
st.push(cur.getLeft());
}
}
//中序遍历 递归
public void inOrderRecursively(TreeNode node){
if(node == null)
return;
inOrderRecursively(node.left);
System.out.print(node.val + " ");
inOrderRecursively(node.right);
}
//中序遍历 非递归
public void inOrderNoRecursively(TreeNode node){
if(node == null)
return;
Stack<TreeNode> st = new Stack<>();
TreeNode cur = node;
while (cur!=null || st.isEmpty()==false){
while (cur != null){
st.push(cur);
cur = cur.getLeft();
}
if(!st.isEmpty()){
cur = st.pop();
System.out.print(cur.getVal()+" ");
cur = cur.getRight();
}
}
}
//后序遍历 递归
public void postOrderRecursively(TreeNode node){
if(node == null)
return;
postOrderRecursively(node.left);
postOrderRecursively(node.right);
System.out.print(node.getVal()+" ");
}
//后序遍历 非递归 可以看作是 先遍历右子树再遍历左子树的前序遍历 的结果 的逆序
public void postOrderNoRecursively(TreeNode node){
if(node == null)
return;
Stack<TreeNode> st = new Stack<>();
Stack<Integer> temp = new Stack<>();
st.push(node);
while (!st.isEmpty()) {
TreeNode cur = st.pop();
temp.push(cur.getVal());
if (cur.getLeft() != null)
st.push(cur.getLeft());
if (cur.getRight() != null)
st.push(cur.getRight());
}
while(!temp.isEmpty()){
System.out.print(temp.pop()+" ");
}
}
//层序遍历 并且给出每个结点的深度
public void levelOrder(TreeNode node){
if(node == null)
return;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(node);
int levelNum = 1;
while (queue.size()!=0){
int len = queue.size();
for (int i = 0; i < len; i++) {
TreeNode temp = queue.poll();
System.out.print(temp.val+" levelNum = "+levelNum+" ");
if(temp.getLeft() != null)
queue.add(temp.getLeft());
if(temp.getRight() != null)
queue.add(temp.getRight());
}
levelNum++;
}
}
//求指定结点值的结点深度
public int levelCount(TreeNode node,int x){
if(node == null)
return -1;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(node);
int levelNum = 1;
while (queue.size()!=0){
int len = queue.size();
for (int i = 0; i < len; i++) {
TreeNode temp = queue.poll();
if(temp.val == x)
return levelNum;
if(temp.getLeft() != null)
queue.add(temp.getLeft());
if(temp.getRight() != null)
queue.add(temp.getRight());
}
levelNum++;
}
return -1;
}
//判断两个结点是否为兄弟结点 兄弟结点:父亲相同的结点
public boolean isBrother(TreeNode node, int x, int y){//x和y为待判断结点
if(node == null)
return false;
if(node.left != null && node.right != null){
if((node.left.val == x && node.right.val == y) || (node.left.val == y && node.right.val == x)){
return true;
}
}
if(isBrother(node.left,x,y))
return true;
if(isBrother(node.right,x,y))
return true;
return false;
}
//判断两个节点是否为堂兄弟结点:堂兄弟结点 深度相同但不是同一个父结点的孩子
public boolean isCousin(TreeNode node, int x, int y){
if(node == null)
return false;
if(levelCount(node, x) != levelCount(node,y))
return false;
else{
if(isBrother(node, x, y))
return false;
else
return true;
}
}
//求树的深度
public int getDepth(TreeNode node){
if(node == null)
return 0;
else {
int hl = getDepth(node.left);
int hr = getDepth(node.right);
return (hr > hr ? hr : hl )+1;
}
}
//获取二叉树的宽度
public int getWidth(TreeNode node){
if(node == null)
return 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(node);
int maxWidth = 1;
while (queue.size()!=0){
int len = queue.size();
for (int i = 0; i < len; i++) {
TreeNode temp = queue.poll();
if(temp.getLeft() != null)
queue.add(temp.getLeft());
if(temp.getRight() != null)
queue.add(temp.getRight());
}
if(len > maxWidth)
maxWidth = len;
}
return maxWidth;
}
//利用前序和中序序列重建二叉树
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if(pre.length == 0 || in.length == 0)
return null;
int len = pre.length-1;
return reConstructBinaryTreeCore(pre,0,len,in,0,len);
}
public TreeNode reConstructBinaryTreeCore(int [] pre, int preStart,int preEnd,int [] in,int inStart,int inEnd){
int rootValue = pre[preStart];
TreeNode root = new TreeNode(rootValue,null,null);
root.left = null;
root.right = null;
if(preStart > preEnd || inStart > inEnd)
return null;
if(preStart == preEnd){
if(inStart == inEnd && pre[preStart]==in[inStart])
return root;
else
return null;
}
int rootInOrder = inStart;
for (int i = inStart; i <= inEnd; i++) {
if(in[i] == rootValue){
rootInOrder = i;
break;
}
}
int leftLength = rootInOrder - inStart ;
int leftPreOrderEnd = preStart + leftLength ;
if(leftLength > 0){
root.left = reConstructBinaryTreeCore(pre,preStart+1,leftPreOrderEnd,in,inStart,rootInOrder-1);
}
if(leftLength < preEnd - preStart){
root.right = reConstructBinaryTreeCore(pre,leftPreOrderEnd+1,preEnd,in,rootInOrder+1,inEnd);
}
return root;
}
public static void main(String[] args) {
TreeNode l2 = new TreeNode(5, null, null);//这五行构造一棵二叉树
TreeNode r2 = new TreeNode(4, null, null);
TreeNode l1 = new TreeNode(3,null, r2);// 根节点左子树
TreeNode r1 = new TreeNode(2, l2, null);// 根节点右子树
TreeNode root = new TreeNode(1, l1, r1);// 创建根节点
binaryTree bt = new binaryTree(root);
System.out.println(" 递归 前序遍历------->");
bt.preOrderRecursively(bt.getRoot());
System.out.println();
System.out.println(" 非递归 前序遍历------->");
bt.preOrderNoRecursively(bt.getRoot());
System.out.println();
System.out.println(" 递归 中序遍历------->");
bt.inOrderRecursively(bt.getRoot());
System.out.println();
System.out.println(" 非递归 中序遍历------->");
bt.inOrderNoRecursively(bt.getRoot());
System.out.println();
System.out.println(" 递归 后序遍历------->");
bt.postOrderRecursively(bt.getRoot());
System.out.println();
System.out.println(" 非递归 后序遍历------->");
bt.postOrderNoRecursively(bt.getRoot());
System.out.println();
System.out.println(" 层序遍历------->");
bt.levelOrder(bt.getRoot());
System.out.println();
System.out.println(" 求指定结点值的深度------->");
int target = 5;
int resIndex = bt.levelCount(bt.getRoot(),5);
System.out.println("resIndex = "+resIndex+" ");
System.out.println(" 判断两个结点是否为兄弟结点------->");
if(bt.isBrother(bt.getRoot(),l1.getVal(),r1.getVal()))
System.out.println("是兄弟结点");
else
System.out.println("不是兄弟结点");
System.out.println(" 判断两个结点是否为堂兄弟结点------->");
if(bt.isCousin(bt.getRoot(),l2.getVal(),r2.getVal()))
System.out.println("是堂兄弟结点");
else
System.out.println("不是堂兄弟结点");
System.out.println(" 求二叉树的深度------->");
System.out.println(bt.getDepth(bt.getRoot()));
System.out.println(" 求二叉树的宽度------->");
System.out.println(bt.getWidth(bt.getRoot()));
System.out.println(" 利用前序和中序序列重建二叉树------->");
// int[] pre = {1,2,4,7,3,5,6,8};
// int[] in = {4,7,2,1,5,3,8,6};
int[] pre = {1,2,4,3};
int[] in = {4,2,1,3};
TreeNode newTree = bt.reConstructBinaryTree(pre,in);
bt.preOrderRecursively(newTree);
}
}