這兩天寫的一個二叉平衡樹
這兩天寫的一個二叉平衡樹
簡介
- 提供了插入、刪除、查詢節點是否存在、獲取樹的size、獲取迭代器的方法和一個層序遍歷的方法
重點:
- 下面平衡樹使用的節點中有一個balance屬性,記錄一個節點是否平衡,不平衡的話是左子樹高還是右子樹高,高多少,balance = 右子樹高度減去左子樹高度(可以是-1,0,1)
- 還有一個side屬性,用於記錄一個節點是父節點的左子節點(-1)還是右子節點(1),還是根節點(0)
下面是原始碼,儘量寫了清晰的註釋
//E 必須要求要 有 hashCode()方法,沒有的話,就會呼叫Object的hashCode()方法,那就大家都相等了
public class Banlance_Tree<E> implements Iterable<E> {
private Node root = null; // 整顆樹的根節點
private int size; //樹中的節點個數
public int size() {
return size;
}
// 內部類
private class Node {
private Node parent;
private Node left;
private Node right;
private E val;
private int balance; //作為每一個節點的平衡因子(等於右子樹高度減去左子樹高度),(可以是-1,0,1)
private int side; //記錄當前節點是父節點的左子節點(-1)還是右子節點(1),當前節點是根節點(0)
// 構造方法一
private Node(E val, int balance, int side) {
this.val = val;
this.balance = balance;
this.side = side;
}
// 構造方法二
private Node(E val, Node parent, int balance, int side) {
this.val = val;
this.parent = parent;
this.balance = balance;
this.side = side;
}
@Override
public String toString() {
String str1 = parent == null ? "null" : String.valueOf(parent.val);
String str2 = left == null ? "null" : String.valueOf(left.val);
String str3 = right == null ? "null" : String.valueOf(right.val);
return "Node{" +
"parent=" + str1 +
", left=" + str2 +
", right=" + str3 +
", val=" + val +
", balance=" + balance +
", side=" + side +
'}';
}
}
// 查詢節點是否存在
public boolean isExit(E val) {
Node node = findNode(val);
return node!=null&&node.val.equals(val); //找到node!=null只能說明兩者的hashCode相等,不能說明兩者是equals的
}
// 插入操作
public boolean insert(E val) {
// 如果該樹為空
if (root == null) {
size++;
root = new Node(val, 0, 0);
return true;
}
// 找到要插入位置的父節點
Node father = findFatherNode(val);
// 插入節點
if (val.hashCode() == father.val.hashCode()) { //樹中存在相同 val的節點,不用插入
return false;
}
// 往下就是一定會插入,那麼先使 size 自增
size++;
if (val.hashCode() > father.val.hashCode()) { //作為右子節點插入
father.right = new Node(val, father, 0, 1);
father.balance++;
} else { //作為左子節點插入
father.left = new Node(val, father, 0, -1);
father.balance--;
}
// 處理旋轉操作
while (true) {
int demoside;
demoside = father.side; //先記錄father的side值
if (father.balance == 0 || father.side == 0) break; //說明插入節點有兄弟節點,那就不會導致樹的不平衡,即不用鬥魚處理了
// 讓father指向插入節點的祖父節點
father = father.parent;
// 處理祖父節點的balance
if (demoside == -1) { //當前節點作為父節點的左節點
father.balance--;
} else if (demoside == 1) { //當前節點作為父節點的右節點 //這裡的判斷條件可以省略嗎?
father.balance++;
}
if (father.balance == 0) { //這一段if else 可以搬到上面的if else 裡面去
break;
} else if (father.balance == -2) {
if (father.left.balance == -1) { //左左情況(左子樹的左子樹造成了father節點的不平衡)
right_roration(father);
} else {
left_roration(father.left); //先對該節點的左子節點左旋
right_roration(father); //再對該節點右旋
}
} else if (father.balance == 2) {
if (father.right.balance == 1) {
left_roration(father);
} else {
right_roration(father.right);
left_roration(father);
}
}
}
return true;
}
// 刪除操作
public boolean delete(E val) {
// 找到該節點
Node node = findNode(val);
if (node == null) return false; //節點沒找到
//往下就是要刪除的節點找到了
size--;
// 記錄被刪除節點
Node rem = node;
// 該節點不是葉子節點
// 找到要刪除的葉子節點
if (node.left != null) {
node = node.left;
while (node.right != null) {
node = node.right;
}
} else if (node.right != null) {
node = node.right;
while (node.left != null) {
node = node.left;
}
}
// 用該葉子節點頂替被刪除節點的位置
rem.val = node.val;
deleteLeaf(node);
return true;
}
// 刪除葉子節點
private void deleteLeaf(Node node) {
// 首先要明確,能走到這裡,被刪除節點node一定是不為null的,且node一定是葉子節點
// 被刪除節點是根節點
if (node.side == 0) {
root = null;
return;
}
// 找到被刪除節點的父節點
Node father = node.parent;
// 被節點刪除
if (node.side == -1) {
father.left = null;
father.balance++;
} else {
father.right = null;
father.balance--;
}
// 處理旋轉操作
while (true) {
if (father.balance == 0) { //以father為根節點的樹的深度減少了一次,那麼繼續往上找
if (father.side == 0) { //該節點為根節點
break;
}
if (father.side == -1) { //該節點是父節點的左子節點
father = father.parent;
father.balance++;
} else { //該節點是父節點的右子節點
father = father.parent;
father.balance--;
}
continue;
}
if (father.balance == -1 || father.balance == 1) { //如果balance變成了1或者-1,那麼balance原來一定是0,那麼由0變成1或者-1是不會改變該節點的深度的,也就不可能破會樹的平衡了
break;
} else if (father.balance == -2) {
assert father.left != null : "當前節點的balance為-2,而此時當前節點沒有左子節點"; // 用一下斷言,實際上是不可能出現一個節點的balance為-2,而又沒有左子節點的
if (father.left.balance == -1) { //左左情況(左子樹的左子樹造成了father節點的不平衡)
right_roration(father); //右旋
} else {
left_roration(father.left); //先對該節點的左子節點左旋
right_roration(father); //再對該節點右旋
}
} else if (father.balance == 2) {
assert father.right != null : "當前節點的balance為-2,而此時當前節點沒有右子節點"; // 用一下斷言,實際上是不可能出現一個節點的balance為-2,而又沒有右子節點的
if (father.right.balance == 1) {
left_roration(father);
} else {
right_roration(father.right);
left_roration(father);
}
}
}
}
// 查詢插入節點的父節點(根據插入節點的val查詢)(如果val以存在,返回值為val的節點)
private Node findFatherNode(E val) {
Node demo = root;
Node oc = null;
while (demo != null) {
oc = demo;
if (val.hashCode() < demo.val.hashCode()) { //往左走
demo = demo.left;
} else if (val.hashCode() > demo.val.hashCode()) { //往右走
demo = demo.right;
} else {
break;
}
}
return oc;
}
// 查詢節點
private Node findNode(E val) {
Node fatherNode = findFatherNode(val);
if (fatherNode != null && fatherNode.val.hashCode() == val.hashCode()) {
return fatherNode;
}
return null;
}
//這裡的①②③④⑤是表示對節點①進行旋轉會影響到的點或周圍的點,不是說樹就長這樣
/*
① ②
↙ ↘ ↙ ↘
② ③ ==> ④ ①
↙ ↘ ↙ ↘
④ ⑤ ⑤ ③
*/
//右旋(左左)
private void right_roration(Node node) {
//node 是 ①
Node father = node.parent; //記錄node的父節點
int side = node.side; //記錄node的side
Node demo = node.left.right; //demo = ⑤
node.left.right = node; //②.right = ①
node.left.parent = node.parent; //②.parent = ①.parent
node.parent = node.left; //①.parent = ②
node.left = demo; //①.left = ⑤
if (demo != null) {
demo.parent = node; //⑤.parent = ①
demo.side = -1;
}
// 處理①和②的balance,①一定會由-2變為0,②一定會由-1變為0
node.balance = 0;
node.parent.balance = 0;
// 處理①和②的side
node.parent.side = node.side;
node.side = 1;
// 原來①為該子樹的根節點,現在要改為②
if (side == 1) { //原根節點是原根節點父節點的右節點
father.right = node.parent;
} else if (side == -1) { //原根節點是原根節點父節點的左節點
father.left = node.parent;
} else { //原根節點沒有父節點,那直接讓node.parent作為root節點
root = node.parent;
}
}
/*
① ③
↙ ↘ ↙ ↘
② ③ ==> ① ⑤
↙ ↘ ↙ ↘
④ ⑤ ② ④
*/
//左旋(右右)
private void left_roration(Node node) {
Node father = node.parent;
int side = node.side;
Node demo = node.right.left;
node.right.left = node;
node.right.parent = node.parent;
node.parent = node.right;
node.right = demo;
if (demo != null) {
demo.parent = node;
demo.side = 1;
}
node.balance = 0;
node.parent.balance = 0;
node.parent.side = node.side;
node.side = -1;
if (side == 1) {
father.right = node.parent;
} else if (side == -1) {
father.left = node.parent;
} else {
root = node.parent;
}
}
// 實現迭代器
// 這裡迭代器按中序遍歷來(反正是對比的hashCode,實際上這個中序遍歷時沒有什麼意義的)
private class InnerIterator implements Iterator<E> {
private List<E> list = new ArrayList<>();
private int index;
private InnerIterator() {
non_recursion(Banlance_Tree.this.root);
}
@Override
public boolean hasNext() {
return index < list.size();
}
@Override
public E next() {
if (hasNext()) return list.get(index++);
return null;
}
@Override
public void remove() {
index++;
}
private void non_recursion(Node root) {
list = new ArrayList<>();
Deque<Node> deque = new LinkedList<>();
while (root != null || !deque.isEmpty()) {
while (root != null) {
deque.push(root);
root = root.left;
}
root = deque.pop();
list.add(root.val);
root = root.right;
}
}
}
@Override
public Iterator<E> iterator() {
return new InnerIterator();
}
// 層次遍歷
public void level_traverse() {
ArrayList<E> oc = new ArrayList<>();
Deque<Node> deque = new LinkedList<>();
Node trav = root;
deque.offer(trav);
while (!deque.isEmpty()) {
trav = deque.poll();
if (trav == null) continue;
oc.add(trav.val);
deque.offer(trav.left);
deque.offer(trav.right);
}
System.out.println(oc);
}
}
相關文章
- 手寫AVL平衡二叉搜尋樹
- 平衡二叉樹二叉樹
- 排序二叉樹和平衡二叉樹排序二叉樹
- 每日一練(28):平衡二叉樹二叉樹
- 平衡二叉樹(AVL)二叉樹
- 平衡樹和二叉樹的區別二叉樹
- 手擼二叉樹——AVL平衡二叉樹二叉樹
- 平衡二叉樹,B樹,B+樹二叉樹
- 平衡二叉樹(AVL樹)和 二叉排序樹轉化為平衡二叉樹 及C語言實現二叉樹排序C語言
- 110. 平衡二叉樹二叉樹
- 自動平衡二叉樹的構建-AVL樹二叉樹
- 平衡二叉樹(AVL樹),原來如此!!!二叉樹
- 平衡二叉查詢樹:紅黑樹
- 十三、Mysql之平衡二叉樹(AVL樹)MySql二叉樹
- 如何判斷一棵樹是否是二叉平衡樹~
- 二叉堆、BST 與平衡樹
- LeetCode-110-平衡二叉樹LeetCode二叉樹
- JZ-039-平衡二叉樹二叉樹
- 夜刷:平衡二叉樹的基本操作二叉樹
- 二叉樹的深度、寬度遍歷及平衡樹二叉樹
- Java實現紅黑樹(平衡二叉樹)Java二叉樹
- 滿二叉樹、完全二叉樹、平衡二叉樹、二叉搜尋樹(二叉查詢樹)和最優二叉樹二叉樹
- 程式碼隨想錄——二叉樹-12.平衡二叉樹二叉樹
- python實現非平衡二叉樹Python二叉樹
- 資料結構-平衡二叉樹資料結構二叉樹
- 二叉平衡樹 python 列表 遞迴Python遞迴
- [Python手撕]判斷平衡二叉樹Python二叉樹
- 學習筆記——二叉平衡樹(BST)筆記
- 遞迴判斷是否二叉平衡樹遞迴
- Python 樹表查詢_千樹萬樹梨花開,忽如一夜春風來(二叉排序樹、平衡二叉樹)Python排序二叉樹
- Java 樹結構實際應用 四(平衡二叉樹/AVL樹)Java二叉樹
- day 15|二叉樹part03|110.平衡二叉樹|257. 二叉樹的所有路徑|404.左葉子之和| 222.完全二叉樹的節點個數二叉樹
- 資料結構之樹結構概述(含滿二叉樹、完全二叉樹、平衡二叉樹、二叉搜尋樹、紅黑樹、B-樹、B+樹、B*樹)資料結構二叉樹
- 這兩天看phpsocket.io,寫了個簡單的web線上聊天PHPWeb
- JZ79 判斷是不是平衡二叉樹二叉樹
- 24. 平衡二叉樹,及其程式碼實現二叉樹
- leetcode-1382. 將二叉搜尋樹變平衡LeetCode
- 二叉樹任意兩個節點間的最大距離(Java,LeetCode 543二叉樹的直徑 遞迴)二叉樹JavaLeetCode遞迴