Java 樹結構的基礎部分(一)
二叉樹
1.1 為什麼需要樹這種數據結構
1) 數組存儲方式的分析
優點:通過下標方式訪問元素,速度快。對於有序數組,還可使用二分查找提高檢索速度。
缺點:如果要檢索具體某個值,或者插入值(按一定順序)會整體移動,效率較低 [示意圖]
畫出操作示意圖:
2) 鏈式存儲方式的分析
優點:在一定程度上對數組存儲方式有優化(比如:插入一個數值節點,只需要將插入節點,鏈接到鏈表中即可,
刪除效率也很好)。
缺點:在進行檢索時,效率仍然較低,比如(檢索某個值,需要從頭節點開始遍歷) 【示意圖】
操作示意圖:
3) 樹存儲方式的分析
能提高數據存儲,讀取的效率, 比如利用 二叉排序樹(Binary Sort Tree),既可以保證數據的檢索速度,同時也
可以保證數據的插入,刪除,修改的速度。【示意圖,後面詳講】
案例: [7, 3, 10, 1, 5, 9, 12]
1.2 樹示意圖
樹的常用術語(結合示意圖理解):
1) 節點
2) 根節點
3) 父節點
4) 子節點
5) 葉子節點 (沒有子節點的節點)
6) 節點的權(節點值)
7) 路徑(從 root 節點找到該節點的路線)
8) 層
9) 子樹
10) 樹的高度(最大層數)
11) 森林 :多顆子樹構成森林
1.3 二叉樹的概念
1) 樹有很多種,每個節點最多只能有兩個子節點的一種形式稱為二叉樹。
2) 二叉樹的子節點分為左節點和右節點
3) 示意圖
4) 如果該二叉樹的所有葉子節點都在最後一層,並且結點總數= 2^n -1 , n 為層數,則我們稱為滿二叉樹。
5) 如果該二叉樹的所有葉子節點都在最後一層或者倒數第二層,而且最後一層的葉子節點在左邊連續,倒數第二
層的葉子節點在右邊連續,我們稱為完全二叉樹
1.4 二叉樹遍歷的說明
使用前序,中序和後序對下面的二叉樹進行遍歷.
1) 前序遍歷: 先輸出父節點,再遍歷左子樹和右子樹
2) 中序遍歷: 先遍歷左子樹,再輸出父節點,再遍歷右子樹
3) 後序遍歷: 先遍歷左子樹,再遍歷右子樹,最後輸出父節點
4) 小結: 看輸出父節點的順序,就確定是前序,中序還是後序
1.5 二叉樹遍歷應用實例(前序,中序,後序)
應用實例的說明和思路
程式碼實現
在最後面
1.6 二叉樹-查找指定節點
要求
1) 請編寫前序查找,中序查找和後序查找的方法。
2) 並分別使用三種查找方式,查找 heroNO = 5 的節點
3) 並分析各種查找方式,分別比較了多少次
4) 思路分析圖解
5) 程式碼實現
在最後面
1.7 二叉樹-刪除節點
要求
1) 如果刪除的節點是葉子節點,則刪除該節點
2) 如果刪除的節點是非葉子節點,則刪除該子樹
3) 測試,刪除掉 5 號葉子節點 和 3 號子樹.
4) 完成刪除思路分析
5) 程式碼實現
package com.lin.tree_0308;
public class BinaryTreeDemo {
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
HeroNode heroNode1 = new HeroNode(1, "伍六七");
HeroNode heroNode2 = new HeroNode(2, "梅花十一");
HeroNode heroNode3 = new HeroNode(3, "梅花十三");
HeroNode heroNode4 = new HeroNode(4, "江主任");
HeroNode heroNode5 = new HeroNode(5, "希義");
heroNode1.setLeft(heroNode2);
heroNode1.setRight(heroNode3);
heroNode3.setRight(heroNode4);
heroNode3.setLeft(heroNode5);
binaryTree.setRoot(heroNode1);
// System.out.println("前序遍歷:");
// binaryTree.preOrder();
// System.out.println("中序遍歷:");
// binaryTree.infixOrder();
//
// System.out.println("後序遍歷");
// binaryTree.postOrder();
// System.out.println("前序查找:");
// HeroNode preOrderSearch = binaryTree.preOrderSearch(5);
// if(preOrderSearch != null) {
// System.out.println(preOrderSearch);
// } else {
// System.out.println("沒有找到");
// }
// System.out.println("中序查找:");
// HeroNode infixOrderSearch = binaryTree.infixOrderSearch(5);
// if(infixOrderSearch != null) {
// System.out.println(infixOrderSearch);
// } else {
// System.out.println("沒有找到");
// }
//
// System.out.println("後序查找:");
// HeroNode postOrderSearch = binaryTree.postOrderSearch(5);
// if(postOrderSearch != null) {
// System.out.println(postOrderSearch);
// } else {
// System.out.println("沒有找到");
// }
System.out.println("刪除前");
binaryTree.preOrder();
binaryTree.delNode(2);
System.out.println("刪除後");
binaryTree.preOrder();
}
}
class BinaryTree{
private HeroNode root;
public void setRoot(HeroNode root) {
this.root = root;
}
// 刪除節點
public void delNode(int no) {
if (root != null) {
// 如果只有一個root
if (root.getNo() == no) {
root = null;
} else {
root.delNode(no);
}
} else {
System.out.println("空樹!");
}
}
// 前序遍歷
public void preOrder() {
if(this.root != null) {
this.root.preOrder();
} else {
System.out.println("二叉樹為空!");
}
}
// 中序遍歷
public void infixOrder() {
if(this.root != null) {
this.root.infixOrder();
} else {
System.out.println("二叉樹為空!");
}
}
// 後序遍歷
public void postOrder() {
if(this.root != null) {
this.root.postOrder();
} else {
System.out.println("二叉樹為空!");
}
}
// 前序查找
public HeroNode preOrderSearch(int no) {
if(root != null) {
return root.preOrderSearch(no);
} else {
return null;
}
}
// 中序查找
public HeroNode infixOrderSearch(int no) {
if (root != null) {
return root.infixOrderSearch(no);
} else {
return null;
}
}
// 後序查找
public HeroNode postOrderSearch(int no) {
if (root != null) {
return root.postOrderSearch(no);
} else {
return null;
}
}
}
class HeroNode{
private String name;
private int no;
private HeroNode left;
private HeroNode right;
public HeroNode(int no, String name) {
this.no = no;
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
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;
}
@Override
public String toString() {
return "HeroNode [name=" + name + ", no=" + no + "]";
}
// 前序遍歷
public void preOrder() {
System.out.println(this); // 輸出父節點
if(this.left != null) {
this.left.preOrder();
}
if(this.right != null) {
this.right.preOrder();
}
}
// 中序遍歷
public void infixOrder() {
if (this.left != null) {
this.left.infixOrder();
}
System.out.println(this); // 輸出父節點
if (this.right != null) {
this.right.infixOrder();
}
}
// 前序遍歷
public void postOrder() {
if (this.left != null) {
this.left.postOrder();
}
if (this.right != null) {
this.right.postOrder();
}
System.out.println(this); // 輸出父節點
}
// 前序查找
public HeroNode preOrderSearch(int no) {
System.out.println("1");
// 比較當前節點是不是
if(this.no == no) {
return this;
}
// 1 判斷當前節點的左節點是否為空,如果不為空,則遞歸前序查找
// 2 如果左遞歸前序查找,找到節點,則返回
HeroNode resNode = null;
if(this.left != null) {
resNode = this.left.preOrderSearch(no);
}
if(resNode != null) {// 說明左子樹找到了
return resNode;
}
// 1 左遞歸如果沒有找到,則繼續判斷
// 2 當前節點的右節點是否為空,如果不為空,則繼續向右遞歸前序查找
if(this.right != null) {
resNode = this.right.preOrderSearch(no);
}
// 這時候不管有沒有找到都要返回resNode
return resNode;
}
// 中序查找
public HeroNode infixOrderSearch(int no) {
HeroNode resNode = null;
if(this.left != null) {
resNode = this.left.infixOrderSearch(no);
}
if(resNode != null) {
return resNode;
}
System.out.println("1");
if(this.no == no) {
return this;
}
if(this.right != null) {
resNode = this.right.infixOrderSearch(no);
}
return resNode;
}
// 後序查找
public HeroNode postOrderSearch(int no) {
HeroNode resNode = null;
if(this.left != null) {
resNode = this.left.postOrderSearch(no);
}
if(resNode != null) {
return resNode;
}
if(this.right != null) {
resNode = this.right.postOrderSearch(no);
}
if(resNode != null) {
return resNode;
}
System.out.println("1");
if(this.no == no) {
return this;
}
// 如果都沒有找到
return resNode;
}
/**
*
* @Description:1 因為我們的二叉樹是單向,所以我們是判斷當前節點的子節點是否需要刪除節點,而不是直接去判斷當前節點是否需要刪除節點。<br>
* 2 如果當前節點的左子節點不為空,並且左子節點就是要刪除節點,就將this.left = null;並且就返回(結束遞歸刪除) <br>
* 3 如果當前節點的右子節點不為空,並且右子節點就是要刪除節點,就將this.right = null;並且就返回(結束遞歸刪除) <br>
* 4 如果第2和第3都沒有刪除節點,那麼我們就需要向左子樹進行遞歸刪除<br>
* 5 如果第4補也沒有刪除節點,則向右子樹進行遞歸刪除<br>
* @author LinZM
* @date 2021-3-8 15:17:32
* @version V1.8
*/
public void delNode(int no) {
if(this.left != null && this.left.no == no) {
this.left = null;
return;
}
if(this.right != null && this.right.no == no) {
this.right = null;
return;
}
if(this.left != null) {
this.left.delNode(no);
}
if(this.right != null) {
this.right.delNode(no);
}
}
}
僅供參考,有錯誤還請指出!
有什麼想法,評論區留言,互相指教指教。
覺得不錯的可以點一下右邊的推薦喲