package com.zzb.tree;
import java.io.Serializable;
/**
* @Auther: Administrator
* @Date: 2019/10/5 20:59
* @Description: 二叉樹的
* 前序遍歷
* 中序遍歷
* 後序遍歷
*
* 前序遍歷查找指定節點
* 中序遍歷查找指定節點
* 後序遍歷查找指點節點
*
* 刪除指定節點(刪除規則:如果刪除的節點是葉子節點,則刪除該節點;如果刪除的節點是非葉子節點,則刪除該子樹)
*
* 前序遍歷: 先輸出父節點,再遍歷左子樹和右子樹
* 中序遍歷: 先遍歷左子樹,再輸出父節點,再遍歷右子樹
* 後序遍歷: 先遍歷左子樹,再遍歷右子樹,最後輸出父節點
* 小結: 看輸出父節點的順序,就確定是前序,中序還是後序
*
* 前序遍歷步驟:(每個節點都會執行前序遍歷方法中的三個動作,涉及到遞歸方法的壓棧與彈棧)
* (1)輸出當前節點(初始節點是根節點root節點)
* (2)判斷如果當前節點的左子節點不爲空,則當前節點的左子節點遞歸執行前序遍歷方法進行前序遍歷
* (3)判斷如果當前節點的右子節點不爲空,則當前節點的右子節點遞歸執行前序遍歷方法進行前序遍歷
*
* 中序遍歷步驟:(每個節點都會執行中序遍歷方法中的三個動作,涉及到遞歸方法的壓棧與彈棧)
* (1)判斷如果當前節點的左子節點不爲空,則當前節點的左子節點遞歸執行中序遍歷方法進行中序遍歷
* (2)輸出當前節點(初始節點是根節點root節點)
* (3)判斷如果當前節點的右子節點不爲空,則當前節點的右子節點遞歸執行中序遍歷方法進行中序遍歷
*
* 後序遍歷步驟;(每個節點都會執行後序遍歷方法中的三個動作,涉及到遞歸方法的壓棧與彈棧)
* (1)判斷如果當前節點的左子節點不爲空,則當前節點的左子節點遞歸執行後序遍歷方法進行後序遍歷
* (2)判斷如果當前節點的右子節點不爲空,則當前節點的右子節點遞歸執行後序遍歷方法進行後序遍歷
* (3)輸出當前節點(初始節點是根節點root節點)
*
* 前序遍歷查找指定節點步驟:
* (1)根據節點的id屬性判斷當前節點是否是要查找的節點,是,則返回當前節點
* (2)不是,則判斷如果當前節點的左子節點不爲空,則當前節點的左子節點遞歸執行前序遍歷查找方法
* (3)判斷如果節點的左子節點遞歸執行前序遍歷查找方法找到了指定的節點,則返回,沒找到,則進行當前節點的右子節點的判斷
* (4)判斷如果當前節點的右子節點不爲空,則當前節點的右子節點遞歸執行前序遍歷查找方法
* (5)最後結果,返回null 或者 找到節點
*
* 中序遍歷查找指定節點步驟:
* (1)判斷如果當前節點的左子節點不爲空,則當前節點的左子節點遞歸執行中序遍歷查找方法
* (2)判斷如果節點的左子節點遞歸執行中序遍歷查找方法找到了指定的節點,則返回,沒找到,則進行當前節點判斷
* (3)根據節點的id屬性判斷當前節點是否是要查找的節點,是,則返回當前節點
* (4)不是,則判斷如果當前節點的右子節點不爲空,則當前節點的右子節點遞歸執行中序遍歷查找方法
* (5)最後結果,返回null 或者 找到節點
*
* 後序遍歷查找指定節點步驟:
* (1)判斷如果當前節點的左子節點不爲空,則當前節點的左子節點遞歸執行後序遍歷查找方法
* (2)判斷如果節點的左子節點遞歸執行後序遍歷查找方法找到了指定的節點,則返回,沒找到,則進行當前節點的右子節點的判斷
* (3)判斷如果當前節點的右子節點不爲空,則當前節點的右子節點遞歸執行後序遍歷查找方法,找到,則返回,沒找到,則進行當前節點判斷
* (4)根據節點的id屬性判斷當前節點是否是要查找的節點,是,則返回當前節點
* (5)不是,則返回null(最後結果,返回null 或者 找到節點)
*
*/
public class BinaryTreeDemo01 {
public static void main(String[] args) {
// 前序遍歷
testPreOrder();
// 中序遍歷
testInfixOrder();
// 後序遍歷
testPostOrder();
// 前序遍歷查找節點
testPreOrderSearch();
// 中序遍歷查找節點
testInfixOrderSearch();
// 後序遍歷查找節點
testPostOrderSearch();
// 刪除指定節點
testDelOne();
}
// 測試前序遍歷
private static void testPreOrder() {
//創建二叉樹
BinaryTree binaryTree = new BinaryTree();
//創建二叉樹結點
HeroNode root = new HeroNode(1, "宋江");
HeroNode node2 = new HeroNode(2, "吳用");
HeroNode node3 = new HeroNode(3, "盧俊義");
HeroNode node4 = new HeroNode(4, "林沖");
HeroNode node5 = new HeroNode(5, "關勝");
//本案例手動創建二叉樹,以後使用遞歸的方式創建二叉樹
root.setLeft(node2);
root.setRight(node3);
node3.setLeft(node5);
node3.setRight(node4);
binaryTree.setRoot(root);
// 前序遍歷
System.out.println("----------------前序遍歷----------------");
binaryTree.preOrder();
System.out.println();
/*----------------前序遍歷----------------
HeroNode{id=1, name='宋江'}
HeroNode{id=2, name='吳用'}
HeroNode{id=3, name='盧俊義'}
HeroNode{id=5, name='關勝'}
HeroNode{id=4, name='林沖'}*/
}
// 測試中序遍歷
private static void testInfixOrder() {
//創建二叉樹
BinaryTree binaryTree = new BinaryTree();
//創建二叉樹結點
HeroNode root = new HeroNode(1, "宋江");
HeroNode node2 = new HeroNode(2, "吳用");
HeroNode node3 = new HeroNode(3, "盧俊義");
HeroNode node4 = new HeroNode(4, "林沖");
HeroNode node5 = new HeroNode(5, "關勝");
//本案例手動創建二叉樹,以後使用遞歸的方式創建二叉樹
root.setLeft(node2);
root.setRight(node3);
node3.setLeft(node5);
node3.setRight(node4);
binaryTree.setRoot(root);
// 中序遍歷
System.out.println("----------------中序遍歷----------------");
binaryTree.infixOrder();
System.out.println();
/*----------------中序遍歷----------------
HeroNode{id=2, name='吳用'}
HeroNode{id=1, name='宋江'}
HeroNode{id=5, name='關勝'}
HeroNode{id=3, name='盧俊義'}
HeroNode{id=4, name='林沖'}*/
}
// 測試後序遍歷
private static void testPostOrder() {
//創建二叉樹
BinaryTree binaryTree = new BinaryTree();
//創建二叉樹結點
HeroNode root = new HeroNode(1, "宋江");
HeroNode node2 = new HeroNode(2, "吳用");
HeroNode node3 = new HeroNode(3, "盧俊義");
HeroNode node4 = new HeroNode(4, "林沖");
HeroNode node5 = new HeroNode(5, "關勝");
//本案例手動創建二叉樹,以後使用遞歸的方式創建二叉樹
root.setLeft(node2);
root.setRight(node3);
node3.setLeft(node5);
node3.setRight(node4);
binaryTree.setRoot(root);
// 後序遍歷
System.out.println("----------------後序遍歷----------------");
binaryTree.postOrder();
System.out.println();
/*----------------後序遍歷----------------
HeroNode{id=2, name='吳用'}
HeroNode{id=5, name='關勝'}
HeroNode{id=4, name='林沖'}
HeroNode{id=3, name='盧俊義'}
HeroNode{id=1, name='宋江'}*/
}
// 測試前序遍歷查找節點
private static void testPreOrderSearch() {
//創建二叉樹
BinaryTree binaryTree = new BinaryTree();
//創建二叉樹結點
HeroNode root = new HeroNode(1, "宋江");
HeroNode node2 = new HeroNode(2, "吳用");
HeroNode node3 = new HeroNode(3, "盧俊義");
HeroNode node4 = new HeroNode(4, "林沖");
HeroNode node5 = new HeroNode(5, "關勝");
//本案例手動創建二叉樹,以後使用遞歸的方式創建二叉樹
root.setLeft(node2);
root.setRight(node3);
node3.setLeft(node5);
node3.setRight(node4);
binaryTree.setRoot(root);
// 前序遍歷查找節點
HeroNode resultNode = binaryTree.preOrderSearch(5);
System.out.println(resultNode);
/*HeroNode{id=5, name='關勝'}*/
}
// 測試中序遍歷查找節點
private static void testInfixOrderSearch() {
//創建二叉樹
BinaryTree binaryTree = new BinaryTree();
//創建二叉樹結點
HeroNode root = new HeroNode(1, "宋江");
HeroNode node2 = new HeroNode(2, "吳用");
HeroNode node3 = new HeroNode(3, "盧俊義");
HeroNode node4 = new HeroNode(4, "林沖");
HeroNode node5 = new HeroNode(5, "關勝");
//本案例手動創建二叉樹,以後使用遞歸的方式創建二叉樹
root.setLeft(node2);
root.setRight(node3);
node3.setLeft(node5);
node3.setRight(node4);
binaryTree.setRoot(root);
// 中序遍歷查找節點
HeroNode resultNode = binaryTree.infixOrderSearch(5);
System.out.println(resultNode);
/*HeroNode{id=5, name='關勝'}*/
}
// 測試後序遍歷查找節點
private static void testPostOrderSearch() {
//創建二叉樹
BinaryTree binaryTree = new BinaryTree();
//創建二叉樹結點
HeroNode root = new HeroNode(1, "宋江");
HeroNode node2 = new HeroNode(2, "吳用");
HeroNode node3 = new HeroNode(3, "盧俊義");
HeroNode node4 = new HeroNode(4, "林沖");
HeroNode node5 = new HeroNode(5, "關勝");
//本案例手動創建二叉樹,以後使用遞歸的方式創建二叉樹
root.setLeft(node2);
root.setRight(node3);
node3.setLeft(node5);
node3.setRight(node4);
binaryTree.setRoot(root);
// 後序遍歷查找節點
HeroNode resultNode = binaryTree.postOrderSearch(5);
System.out.println(resultNode);
/*HeroNode{id=5, name='關勝'}*/
}
// 測試刪除指定節點
private static void testDelOne() {
//創建二叉樹
BinaryTree binaryTree = new BinaryTree();
//創建二叉樹結點
HeroNode root = new HeroNode(1, "宋江");
HeroNode node2 = new HeroNode(2, "吳用");
HeroNode node3 = new HeroNode(3, "盧俊義");
HeroNode node4 = new HeroNode(4, "林沖");
HeroNode node5 = new HeroNode(5, "關勝");
//本案例手動創建二叉樹,以後使用遞歸的方式創建二叉樹
root.setLeft(node2);
root.setRight(node3);
node3.setLeft(node5);
node3.setRight(node4);
binaryTree.setRoot(root);
// 刪除指定節點
binaryTree.delOne(5);
// 前序遍歷
System.out.println("----------------刪除指定節點後的前序遍歷----------------");
binaryTree.preOrder();
System.out.println();
/*----------------刪除指定節點後的前序遍歷----------------
HeroNode{id=1, name='宋江'}
HeroNode{id=2, name='吳用'}
HeroNode{id=3, name='盧俊義'}
HeroNode{id=4, name='林沖'}*/
}
}
// 二叉樹
class BinaryTree {
private HeroNode root;
public HeroNode getRoot() {
return root;
}
public void setRoot(HeroNode root) {
this.root = root;
}
// 前序遍歷
public void preOrder() {
if(this.getRoot() == null) {
System.out.println("二叉樹爲空,無法遍歷!");
} else {
this.getRoot().preOrder();
}
}
// 中序遍歷
public void infixOrder() {
if(this.getRoot() == null) {
System.out.println("二叉樹爲空,無法遍歷!");
} else {
this.getRoot().infixOrder();
}
}
// 後序遍歷
public void postOrder() {
if(this.getRoot() == null) {
System.out.println("二叉樹爲空,無法遍歷!");
} else {
this.getRoot().postOrder();
}
}
// 前序遍歷查找節點
public HeroNode preOrderSearch(Integer id) {
if(this.getRoot() == null) {
return null;
} else {
return this.getRoot().preOrderSearch(id);
}
}
// 中序遍歷查找節點
public HeroNode infixOrderSearch(Integer id) {
if(this.getRoot() == null) {
return null;
} else {
return this.getRoot().infixOrderSearch(id);
}
}
// 後序遍歷查找節點
public HeroNode postOrderSearch(Integer id) {
if(this.getRoot() == null) {
return null;
} else {
return this.getRoot().postOrderSearch(id);
}
}
// 刪除指定節點
public void delOne(Integer id) {
if(this.getRoot() == null) {
System.out.println("空樹,不能執行刪除操作!");
} else {
// 如果只有一個根節點root結點, 這裏立即判斷root是不是就是要刪除結點
if(this.getRoot().getId() == id) {
this.setRoot(null);
} else {
this.getRoot().delOne(id);
}
}
}
}
// 英雄節點類
class HeroNode implements Serializable {
private static final long serialVersionUID = -8283217516456007620L;
private Integer id;
private String name;
private HeroNode left; // 默認爲null
private HeroNode right; // 默認爲null
public HeroNode() {
}
public HeroNode(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HeroNode getLeft() {
return left;
}
public void setLeft(HeroNode left) {
this.left = left;
}
public HeroNode getRight() {
return right;
}
public void setRight(HeroNode right) {
this.right = right;
}
/**
* 前序遍歷
* 每個節點都會執行前序遍歷方法中的三個動作,涉及到遞歸方法的壓棧與彈棧
*/
public void preOrder() {
// (1)輸出當前節點(初始節點是根節點root節點)
System.out.println(this);
// (2)判斷如果當前節點的左子節點不爲空,則當前節點的左子節點遞歸執行前序遍歷方法進行前序遍歷
if(this.getLeft() != null) {
this.getLeft().preOrder();
}
// (3)判斷如果當前節點的右子節點不爲空,則當前節點的右子節點遞歸執行前序遍歷方法進行前序遍歷
if(this.getRight() != null) {
this.getRight().preOrder();
}
}
/**
* 中序遍歷
* 每個節點都會執行中序遍歷方法中的三個動作,涉及到遞歸方法的壓棧與彈棧
*/
public void infixOrder() {
// (1)判斷如果當前節點的左子節點不爲空,則當前節點的左子節點遞歸執行中序遍歷方法進行中序遍歷
if(this.getLeft() != null) {
this.getLeft().infixOrder();
}
// (2)輸出當前節點(初始節點是根節點root節點)
System.out.println(this);
// (3)判斷如果當前節點的右子節點不爲空,則當前節點的右子節點遞歸執行中序遍歷方法進行中序遍歷
if(this.getRight() != null) {
this.getRight().infixOrder();
}
}
/**
* 後序遍歷
* 每個節點都會執行後序遍歷方法中的三個動作,涉及到遞歸方法的壓棧與彈棧
*/
public void postOrder() {
// (1)判斷如果當前節點的左子節點不爲空,則當前節點的左子節點遞歸執行後序遍歷方法進行後序遍歷
if(this.getLeft() != null) {
this.getLeft().postOrder();
}
// (2)判斷如果當前節點的右子節點不爲空,則當前節點的右子節點遞歸執行後序遍歷方法進行後序遍歷
if(this.getRight() != null) {
this.getRight().postOrder();
}
// (3)輸出當前節點(初始節點是根節點root節點)
System.out.println(this);
}
/**
* 根據節點id屬性值前序遍歷查找節點
* @param id 被查找節點的id屬性
* @return 找到則返回,沒找到則返回null
*/
public HeroNode preOrderSearch(Integer id) {
// 判斷當前節點是否是要查找的節點
if(this.getId() == id) {
return this;
}
HeroNode resultNode = null;
// 判斷如果當前節點的左子節點不爲空,則當前節點的左子節點遞歸執行前序遍歷查找方法
if(this.getLeft() != null) {
resultNode = this.getLeft().preOrderSearch(id);
}
// 判斷如果節點的左子節點遞歸執行前序遍歷查找方法找到了指定的節點,則返回
// 沒找到,則進行當前節點的右子節點的判斷
if(resultNode != null) {
return resultNode;
}
// 判斷如果當前節點的右子節點不爲空,則當前節點的右子節點遞歸執行前序遍歷查找方法
if(this.getRight() != null) {
resultNode = this.getRight().preOrderSearch(id);
}
// 返回null 或者 找到節點
return resultNode;
}
/**
* 根據節點id屬性值中序遍歷查找節點
* @param id 被查找節點的id屬性
* @return 找到則返回,沒找到則返回null
*/
public HeroNode infixOrderSearch(Integer id) {
HeroNode resultNode = null;
// 判斷如果當前節點的左子節點不爲空,則當前節點的左子節點遞歸執行中序遍歷查找方法
if(this.getLeft() != null) {
resultNode = this.getLeft().infixOrderSearch(id);
}
// 判斷如果節點的左子節點遞歸執行中序遍歷查找方法找到了指定的節點,則返回
// 沒找到,則進行當前節點判斷
if(resultNode != null) {
return resultNode;
}
// 判斷當前節點是否是要查找的節點
if(this.getId() == id) {
return this;
}
// 判斷如果當前節點的右子節點不爲空,則當前節點的右子節點遞歸執行中序遍歷查找方法
if(this.getRight() != null) {
resultNode = this.getRight().infixOrderSearch(id);
}
// 返回null 或者 找到節點
return resultNode;
}
/**
* 根據節點id屬性值後序遍歷查找節點
* @param id 被查找節點的id屬性
* @return 找到則返回,沒找到則返回null
*/
public HeroNode postOrderSearch(Integer id) {
HeroNode resultNode = null;
// 判斷如果當前節點的左子節點不爲空,則當前節點的左子節點遞歸執行後序遍歷查找方法
if(this.getLeft() != null) {
resultNode = this.getLeft().postOrderSearch(id);
}
// 判斷如果節點的左子節點遞歸執行後序遍歷查找方法找到了指定的節點,則返回
// 沒找到,則進行當前節點的右子節點的判斷
if(resultNode != null) {
return resultNode;
}
// 判斷如果當前節點的右子節點不爲空,則當前節點的右子節點遞歸執行後序遍歷查找方法
if(this.getRight() != null) {
resultNode = this.getRight().postOrderSearch(id);
}
// 判斷當前節點是否是要查找的節點
if(this.getId() == id) {
return this;
}
// 返回null 或者 找到節點
return resultNode;
}
/**
* 遞歸刪除結點
* 1、如果刪除的節點是葉子節點,則刪除該節點
* 2、如果刪除的節點是非葉子節點,則刪除該子樹
* @param id 待刪除節點的id屬性值
*/
public void delOne(Integer id) {
/*1、因爲我們的二叉樹是單向的,所以我們是判斷當前結點的子結點是否需要刪除結點,而不能去判斷當前這個結點是不是需要刪除結點
2、如果當前結點的左子結點不爲空,並且左子結點就是要刪除結點,就將this.left = null,並且就返回(結束遞歸刪除)
3、如果當前結點的右子結點不爲空,並且右子結點就是要刪除結點,就將this.right= null,並且就返回(結束遞歸刪除)
4、如果第2和第3步沒有刪除結點,那麼我們就需要向左子樹進行遞歸刪除
5、如果第4步也沒有刪除結點,則應當向右子樹進行遞歸刪除*/
// 2、如果當前結點的左子結點不爲空,並且左子結點就是要刪除結點,就將this.left = null,並且就返回(結束遞歸刪除)
if(this.getLeft() != null && this.getLeft().getId() == id) {
this.setLeft(null);
return;
}
// 3、如果當前結點的右子結點不爲空,並且右子結點就是要刪除結點,就將this.right= null,並且就返回(結束遞歸刪除)
if(this.getRight() != null && this.getRight().getId() == id) {
this.setRight(null);
return;
}
// 4、如果第2和第3步沒有刪除結點,那麼我們就需要向左子樹進行遞歸刪除
if(this.getLeft() != null) {
this.getLeft().delOne(id);
}
// 5、如果第4步也沒有刪除結點,則應當向右子樹進行遞歸刪除
if(this.getRight() != null) {
this.getRight().delOne(id);
}
}
@Override
public String toString() {
return "HeroNode{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}