Java實作二分搜尋樹
- 二分搜尋樹的定義
-
- 二分搜尋樹的代碼結構
- 向二分搜尋樹添加元素(遞歸實作)
- 對添加元素代碼的簡化(如果遞歸功底深厚)
- 檢視二分搜尋樹中是否還有元素e(遞歸)
- 二分搜尋樹的前序周遊(遞歸)
- 基于前序周遊重寫toString
- 二分搜尋樹的中序周遊
- 二分搜尋樹的後序周遊
- 二分搜尋樹的前序周遊(非遞歸)
- 二分搜尋樹的層序周遊(非遞歸)(廣度優先周遊)
- 查找二分搜尋樹中的最大最小值
- 删除二分搜尋樹的最小值與最大值
- 删除二分搜尋樹的任意元素
- 删除任意一個元素
- 通篇代碼整合(終結版)
二分搜尋樹的定義
#二分搜尋樹是二叉樹
#二分搜尋樹的每個節點的值:
1.大于左子樹的所有節點的值
2.小于右子樹的所有節點的值
#存儲的元素要具有可比較性。例如:如果存儲學生對象,可以比較學号。
二分搜尋樹的代碼結構
public class BST<E extends Comparable<E>> { private Node root;
private int size;
private class Node {
public E e;
public Node left, right;
public Node(E e) {
this.e = e;
this.left = null;
this.right = null;
}
}
public BST() {
this.root = null;
this.size = 0;
}
public int getSize() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
}
向二分搜尋樹添加元素(遞歸實作)
public void add(E e) {
if (root == null) {
root = new Node(e);
size++;
} else
add(root, e);
}
private void add(Node node, E e) {
//遞歸終止條件
if (e.equals(node.e))
return;
else if (e.compareTo(node.e) < 0 && node.left == null) {
node.left = new Node(e);
size++;
return;
} else if (e.compareTo(node.e) > 0 && node.right == null) {
node.left = new Node(e);
size++;
return;
}
//遞歸調用
if (e.compareTo(node.e) < 0)
add(node.left, e);
else//e.compareTo(node.e) > 0
add(node.right, e);
}
對添加元素代碼的簡化(如果遞歸功底深厚)
public void add(E e) {
root = add(root, e);
}
private Node add(Node node, E e) {
if (node == null) {
size++;
return new Node(e);
}
if (e.compareTo(node.e) < 0)
node.left = add(node.left, e);
else if (e.compareTo(node.e) > 0)
node.right = add(node.right, e);
return node;
}
檢視二分搜尋樹中是否還有元素e(遞歸)
public boolean contains(E e) {
return contains(root, e);
}
private boolean contains(Node node, E e) {
if (node == null)
return false;
if (e.compareTo(node.e) == 0)
return true;
else if (e.compareTo(node.e) < 0)
return contains(node.left, e);
else //e.compareTo(node.e) > 0
return contains(node.right, e);
}
二分搜尋樹的前序周遊(遞歸)
public void preorderTraversal() {
preorderTraversal(root);
}
private void preorderTraversal(Node node) {
if (node == null)
return;
System.out.println(node.e);
preorderTraversal(node.left);
preorderTraversal(node.right);
}
基于前序周遊重寫toString
@Override
public String toString() {
StringBuilder res = new StringBuilder();
generateBSTString(root, 0, res);
return res.toString();
}
private void generateBSTString(Node node, int depth, StringBuilder res) {
if (node == null) {
res.append(generateDepthString(depth) + "Null\n");
return;
}
res.append(generateDepthString(depth)+node.e + "\n");
generateBSTString(node.left,depth+1,res);
generateBSTString(node.right,depth+1,res);
}
private String generateDepthString(int depth) {
StringBuilder res = new StringBuilder();
for (int i = 0; i < depth; i++)
res.append("-");
return res.toString();
}
二分搜尋樹的中序周遊
public void intermediateTraversal(){
intermediateTraversal(root);
}
private void intermediateTraversal(Node node) {
if (node==null)
return;
intermediateTraversal(node.left);
System.out.println(node.e);
intermediateTraversal(node.right);
}
二分搜尋樹的後序周遊
public void postorderTraversal(){
postorderTraversal(root);
}
private void postorderTraversal(Node node) {
if (node==null)
return;
postorderTraversal(node.left);
postorderTraversal(node.right);
System.out.println(node.e);
}
二分搜尋樹的前序周遊(非遞歸)
說明:
1.先将根節點壓入棧,出棧,記錄,并将左右孩子節點壓入(先壓入右孩子節點,後壓入左孩子節點)
2.出棧(即左孩子節點(棧頂元素)先出棧),記錄,并檢視左孩子節點是否有孩子節點,仍然是右孩子節點先壓入棧,然後壓入左孩子節點。如果沒有孩子,則出棧現在棧頂元素(右孩子節點),記錄。
3.循環以上過程
如下圖:
28入棧,出棧,記錄。30,16入棧,16為棧頂,出棧,記錄,并将16的左右孩子節點13和22入棧。13為棧頂,出棧,此時13沒有左右孩子節點,22出棧,記錄。22沒有左右孩子,30出棧,記錄。
壓入30的左右孩子節點。此時,棧頂元素為30的左孩子節點,即29,29出棧,記錄。由于29沒有孩子節點,42出棧,記錄。42沒有孩子節點,并且此時棧為空,前序周遊結束。
代碼實作:
public void preorderTraversalNR() {
Stack<Node> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
Node cur = stack.pop();
System.out.println(cur.e);
if (cur.right != null)
stack.push(cur.right);
if (cur.left != null)
stack.push(cur.left);
}
}
由于使用了java中的棧,需要在代碼前加:
import java.util.Stack;
二分搜尋樹的層序周遊(非遞歸)(廣度優先周遊)
說明:(使用輔助資料結構:隊列)
1.先将根節點入隊,出隊,記錄。并将根節點的左右孩子節點入隊(左孩子節點先入隊,右孩子節點後入隊)
2.入隊完成後,隊首元素出隊,記錄,并将其左右孩子入隊(仍然按照左先右後的順序)
3.由于隊列先進先出的性質,每次都是左孩子節點先出隊,右孩子後出隊。并且左孩子節點和右孩子節點都入隊,左孩子節點出隊後,壓入左孩子節點的左右孩子節點之前,上一層右孩子節點為隊首,是以下層的左右孩子節點在上層的右孩子節點之後,是以可以做到層序周遊。
如下圖:
根節點28入隊,出隊并記錄,并将左右孩子節點16,30入隊。
左孩子節點16出隊,記錄。并将16的左右孩子節點13,22入隊。
此時30位隊首元素,出隊,記錄,并入隊30的左右孩子節點。
此時隊首元素為13,出隊,記錄,入隊13的左右孩子節點,由于沒有孩子節點,是以此時什麼都不做。然後去隊首,出隊隊首元素22,沒有孩子節點,出隊,記錄,沒有孩子節點,什麼都不做。出隊隊首元素29,記錄,沒有孩子節點,什麼都不做。出隊隊首元素42,記錄。沒有孩子節點,什麼都不做。出隊隊首元素,由于此時隊列為空,周遊結束。
代碼實作:
public void levelTraversal(){
Queue<Node> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()){
Node cur = queue.remove();
System.out.println(cur.e);
if (cur.left!=null)
queue.add(cur.left);
if (cur.right!=null)
queue.add(cur.right);
}
}
查找二分搜尋樹中的最大最小值
最小值:一直向左一直到null之前
最大值:一直向右一直到null之前
情況1:
情況2:
#查找最小值(遞歸)
public E minimumValue(){
if (size == 0)
throw new IllegalArgumentException("BST is Empty!");
return minimumValue(root);
}
private E minimumValue(Node node) {
if (node.left==null)
return node.e;
return minimumValue(node.left);
}
#查找最小值(非遞歸)
public E minimumValueNR() {
if (size == 0)
throw new IllegalArgumentException("BST is Empty!");
Node cur = root;
while (cur.left!=null)
cur = cur.left;
return cur.e;
}
#查找最大值(遞歸)
public E maximumValue(){
if (size == 0)
throw new IllegalArgumentException("BST is Empty!");
return maximumValue(root);
}
private E maximumValue(Node node) {
if (node.right==null)
return node.e;
return maximumValue(node.right);
}
#查找最大值(非遞歸)
public E maximumValueNR() {
if (size == 0)
throw new IllegalArgumentException("BST is Empty!");
Node cur = root;
while (cur.right != null)
cur = cur.right;
return cur.e;
}
删除二分搜尋樹的最小值與最大值
#删除最小值
情況1:最小值為葉子節點(如下圖)
操作:直接删除就好了
情況2:最小值為非葉子節點(如下圖)
操作:将删除節點的右子樹變成删除節點的父親節點的左子樹
情況2删除後:(如下圖)
#删除最小值(遞歸)
public E removeMin() {
E ret = minimumValueNR();
root = removeMin(root);
return ret;
}
//删除以node為根的最小值
//傳回删除節點後新的二分搜尋樹的跟
private Node removeMin(Node node) {
if (node.left==null){
Node rightNode = node.right;
node.right=null;
size--;
return rightNode;
}
node.left = removeMin(node.left);
return node;
}
#删除最小值(非遞歸)
public E removeMinNR() {
E ret = minimumValueNR();
Node cur = root;
if (cur.left == null) {
Node rightNode = cur.right;
cur.right = null;
root = rightNode;
size--;
return ret;
}
while (cur.left.left != null) {
cur = cur.left;
}
if (cur.left.right != null) {
Node rightNode = cur.left.right;
cur.left = rightNode;
} else
cur.left = null;
size--;
return ret;
}
#删除最大值
情況1:最大值為葉子節點(如下圖)
操作:直接删除就好了
情況二:最大值為非葉子節點(如下圖)
操作:将删除節點的左子樹變成删除節點的父親節點的右子樹
情況2删除後(如下圖):
#删除最大值(遞歸)
public E removeMax() {
E ret = maximumValueNR();
root = removeMax(root);
return ret;
}
//删除以node為根的最大值
//傳回删除節點後新的二分搜尋樹的跟
private Node removeMax(Node node) {
if (node.right == null) {
Node leftNode = node.left;
node.left = null;
size--;
return leftNode;
}
node.right = removeMax(node.right);
return node;
}
#删除最大值(非遞歸)
public E removeMaxNR() {
E ret = maximumValueNR();
Node cur = root;
if (cur.right == null) {
Node leftNode = cur.left;
cur.left = null;
root = leftNode;
size--;
return ret;
}
while (cur.right.right != null)
cur = cur.right;
if (cur.right.left != null) {
Node leftNode = cur.right.left;
cur.right = leftNode;
} else
cur.right = null;
size--;
return ret;
}
删除二分搜尋樹的任意元素
删除任意一個元素
public void remove(E e) {
root = remove(root, e);
}
private Node remove(Node node, E e) {
if (node == null)
return null;
if (e.compareTo(node.e) < 0) {
node.left = remove(node.left, e);
return node;
} else if (e.compareTo(node.e) > 0) {
node.right = remove(node.right, e);
return node;
} else {//e.compareTo(node.e) == 0
if (node.left == null) {
Node rightNode = node.right;
node.right = null;
size--;
return rightNode;
}
if (node.right == null) {
Node leftNode = node.left;
node.left = null;
size--;
return leftNode;
}
Node successor = minimumValue(node.right);
successor.right = removeMin(node.right);
successor.left = node.left;
node.left = node.right = null;
return successor;
}
}
通篇代碼整合(終結版)
package com.cc;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
/**
* @Author: 李立建
* @Date: 2019/7/26 22:03
* @Version 1.0
*/
public class BST<E extends Comparable<E>> {
private Node root;
private int size;
private class Node {
public E e;
public Node left, right;
public Node(E e) {
this.e = e;
this.left = null;
this.right = null;
}
@Override
public String toString() {
return e.toString();
}
}
public BST() {
this.root = null;
this.size = 0;
}
public int getSize() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public void add(E e) {
root = add(root, e);
}
private Node add(Node node, E e) {
if (node == null) {
size++;
return new Node(e);
}
if (e.compareTo(node.e) < 0)
node.left = add(node.left, e);
else if (e.compareTo(node.e) > 0)
node.right = add(node.right, e);
return node;
}
public boolean contains(E e) {
return contains(root, e);
}
private boolean contains(Node node, E e) {
if (node == null)
return false;
if (e.compareTo(node.e) == 0)
return true;
else if (e.compareTo(node.e) < 0)
return contains(node.left, e);
else //e.compareTo(node.e) > 0
return contains(node.right, e);
}
public void preorderTraversal() {
preorderTraversal(root);
}
private void preorderTraversal(Node node) {
if (node == null)
return;
System.out.println(node.e);
preorderTraversal(node.left);
preorderTraversal(node.right);
}
public void preorderTraversalNR() {
Stack<Node> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
Node cur = stack.pop();
System.out.println(cur.e);
if (cur.right != null)
stack.push(cur.right);
if (cur.left != null)
stack.push(cur.left);
}
}
public void intermediateTraversal() {
intermediateTraversal(root);
}
private void intermediateTraversal(Node node) {
if (node == null)
return;
intermediateTraversal(node.left);
System.out.println(node.e);
intermediateTraversal(node.right);
}
public void postorderTraversal() {
postorderTraversal(root);
}
private void postorderTraversal(Node node) {
if (node == null)
return;
postorderTraversal(node.left);
postorderTraversal(node.right);
System.out.println(node.e);
}
public void levelTraversal() {
Queue<Node> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
Node cur = queue.remove();
System.out.println(cur.e);
if (cur.left != null)
queue.add(cur.left);
if (cur.right != null)
queue.add(cur.right);
}
}
public E minimumValue() {
if (size == 0)
throw new IllegalArgumentException("BST is Empty!");
return minimumValue(root).e;
}
private Node minimumValue(Node node) {
if (node.left == null)
return node;
return minimumValue(node.left);
}
public E minimumValueNR() {
if (size == 0)
throw new IllegalArgumentException("BST is Empty!");
Node cur = root;
while (cur.left != null)
cur = cur.left;
return cur.e;
}
public E maximumValue() {
if (size == 0)
throw new IllegalArgumentException("BST is Empty!");
return maximumValue(root);
}
private E maximumValue(Node node) {
if (node.right == null)
return node.e;
return maximumValue(node.right);
}
public E maximumValueNR() {
if (size == 0)
throw new IllegalArgumentException("BST is Empty!");
Node cur = root;
while (cur.right != null)
cur = cur.right;
return cur.e;
}
public E removeMin() {
E ret = minimumValueNR();
root = removeMin(root);
return ret;
}
public Node removeMin(Node node) {
if (node.left == null) {
Node rightNode = node.right;
node.right = null;
size--;
return rightNode;
}
node.left = removeMin(node.left);
return node;
}
public E removeMinNR() {
E ret = minimumValueNR();
Node cur = root;
if (cur.left == null) {
Node rightNode = cur.right;
cur.right = null;
root = rightNode;
size--;
return ret;
}
while (cur.left.left != null) {
cur = cur.left;
}
if (cur.left.right != null) {
Node rightNode = cur.left.right;
cur.left = rightNode;
} else
cur.left = null;
size--;
return ret;
}
public E removeMax() {
E ret = maximumValueNR();
root = removeMax(root);
return ret;
}
private Node removeMax(Node node) {
if (node.right == null) {
Node leftNode = node.left;
node.left = null;
size--;
return leftNode;
}
node.right = removeMax(node.right);
return node;
}
public E removeMaxNR() {
E ret = maximumValueNR();
Node cur = root;
if (cur.right == null) {
Node leftNode = cur.left;
cur.left = null;
root = leftNode;
size--;
return ret;
}
while (cur.right.right != null)
cur = cur.right;
if (cur.right.left != null) {
Node leftNode = cur.right.left;
cur.right = leftNode;
} else
cur.right = null;
size--;
return ret;
}
public void remove(E e) {
root = remove(root, e);
}
private Node remove(Node node, E e) {
if (node == null)
return null;
if (e.compareTo(node.e) < 0) {
node.left = remove(node.left, e);
return node;
} else if (e.compareTo(node.e) > 0) {
node.right = remove(node.right, e);
return node;
} else {//e.compareTo(node.e) == 0
if (node.left == null) {
Node rightNode = node.right;
node.right = null;
size--;
return rightNode;
}
if (node.right == null) {
Node leftNode = node.left;
node.left = null;
size--;
return leftNode;
}
Node successor = minimumValue(node.right);
successor.right = removeMin(node.right);
successor.left = node.left;
node.left = node.right = null;
return successor;
}
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
generateBSTString(root, 0, res);
return res.toString();
}
private void generateBSTString(Node node, int depth, StringBuilder res) {
if (node == null) {
res.append(generateDepthString(depth) + "Null\n");
return;
}
res.append(generateDepthString(depth) + node.e + "\n");
generateBSTString(node.left, depth + 1, res);
generateBSTString(node.right, depth + 1, res);
}
private String generateDepthString(int depth) {
StringBuilder res = new StringBuilder();
for (int i = 0; i < depth; i++)
res.append("-");
return res.toString();
}
}