普通的二叉搜尋樹總結+java實現
目錄
二叉搜尋樹的概念
二叉搜尋樹(Binary Search Tree)節點放置:任何節點的鍵值一定大於其左子樹的每一個節點的鍵值,並小於其右子樹的每一個節點的鍵值。
因此,找出BST樹的最大元素和最小元素,就是從根節點一直往左走,直至無左路可走,即得最小元素;從根節點一直往右走,直至無右路可走,即得最大元素。
結構
節點類
有左右節點和值,還有一個列印節點的方法,方便使用
package datastructure.tree.binarysearchtree; import java.util.LinkedList; import java.util.Queue; public class TreeNode { int val; public TreeNode left; public TreeNode right; public TreeNode(int x) { val = x; left=null; right=null; } //通過佇列 當前下層節點個數 列印每層的節點和null,為了方便列印 public static void printTree(TreeNode root){ if(root == null) return; Queue<TreeNode> queue = new LinkedList<TreeNode>(); int current;//當前層 還未列印的結點個數 int next;//下一層結點個數 queue.offer(root); current = 1; next = 0; while(!queue.isEmpty()){ TreeNode currentNode = queue.poll(); if (currentNode!=null) { System.out.print(currentNode.val+" "); current--; } else{ System.out.print("null "); current--; queue.offer(null); next++; queue.offer(null); next++; if(current ==0){ System.out.println(); current = next; next = 0; int temp=0; for (TreeNode treeNode : queue) { if(treeNode==null){ temp++; } } if(temp==current){ System.out.println("end"); break; } } continue; } if(currentNode.left != null){ queue.offer(currentNode.left); next++; } else{ queue.offer(null); next++; } if(currentNode.right != null){ queue.offer(currentNode.right); next++; } else{ queue.offer(null); next++; } if(current ==0){ System.out.println(); current = next; next = 0; int temp=0; for (TreeNode treeNode : queue) { if(treeNode==null){ temp++; } } if(temp==current){ System.out.println("end"); break; } } } } }
二叉搜尋樹類
一個tree中有一個節點root,其他全是方法
public class BinarySearchTree {
public TreeNode root;
實現-增刪查
插入節點
BST樹插入新元素時,從根節點開始,遇到鍵值較大的值就向左,遇到較小的就向右,一直到尾端,就是插入點。
插入節點,如果已存在相同的樹,則不插入,返回已有的節點,否則返回插入的節點
將now=root 將now的值與x比較,相同返回now,x大,則如果now無右節點,建立右節點x並返回,否則now=now.right
x小則反之亦然
//插入節點,如果已存在相同的樹,則不插入,返回已有的節點,否則返回插入的節點 //將now=root 將now的值與x比較,相同返回now,x大,則如果now無右節點,建立右節點x並返回,否則now=now.right //x小則反之亦然 public TreeNode insertNode(int x){ if(root==null){ root=new TreeNode(x); return root; } TreeNode now=root; while(true){ if(now.val==x){ return now; } if(now.val>x){ if(now.left==null){ now.left=new TreeNode(x); return now.left; } else{ now=now.left; continue; } } else{ if(now.right==null){ now.right=new TreeNode(x); return now.right; } else{ now=now.right; continue; } } } }
建立二叉搜尋樹
只有一個int,則root為x的節點
x為陣列,如果長度為0,初始化root為0的節點
否則逐個插入x的元素
//只有一個int,則root為x的節點
public BinarySearchTree(int x){
root=new TreeNode(x);
}
//x為陣列,如果長度為0,初始化root為0的節點
//否則逐個插入x的元素
public BinarySearchTree(int[] x){
int length=x.length;
if(length==0){
root=new TreeNode(0);
return;
}
for(int i=0;i<length;i++){
insertNode(x[i]);
}
}
搜尋節點
根據now與x的大小,進入now的左右子節點,直到找到now.val==x或者now==null
//根據int x查詢二叉搜尋樹的節點,如果沒找到 返回null,找到了,返回該節點
//now=root 進入true的迴圈 如果now=null,說明沒有返回null,now的值=x,說明找到,返回now
//否則根據now與x的大小,now=now的左右子節點
public TreeNode findNode(int x){
if(root==null){
return null;
}
TreeNode now=root;
while(true){
if(now==null){
return null;
}
if(now.val==x){
return now;
}
if(now.val<x){
now=now.right;
}
else {
now=now.left;
}
}
}
刪除節點
刪除節點首先要找到節點,還要找到他的父親節點,並記錄下是父親節點的左還是右節點
TreeNode now=root;
TreeNode parent=root;
boolean isLeft=true;
while(true){
if(now==null){
return;
}
if(now.val==x){
break;
}
if(now.val<x){
parent=now;
now=now.right;
isLeft=false;
}
else {
parent=now;
now=now.left;
isLeft=true;
}
}
刪除節點分兩種大情況,每種各有三種情況
如果刪除的是普通節點
如果該節點沒有左右節點
直接parent的左右節點為null即可
if(now.left==null&&now.right==null){
if(isLeft){
parent.left=null;
return;
}
else{
parent.right=null;
return;
}
}
如果只有左或者右節點
如果A只有一個子節點,就直接將A的子節點連至A的父節點,並將A刪除;
//如果now沒有左右子節點,則parent的子節點為null,根據isLeft判斷now是parent的左右節點
if(now.left==null&&now.right==null){
if(isLeft){
parent.left=null;
return;
}
else{
parent.right=null;
return;
}
}
//如果now只有左節點,則根據isleft parent的左右節點為now.left
if(now.left!=null&&now.right==null){
if(isLeft){
parent.left=now.left;
return;
}
else{
parent.right=now.left;
return;
}
}
節點左右節點都有
就以右子樹的最小節點(右子樹的最左節點,我選的是這個)或左子樹的最大節點取代A即可。
有兩種方法,一種是修改值,一種是節點left,right改變(我選的是這個),前一個應該簡單一點
//如果now左右節點都有,則根據isleft parent的左右節點為now的右節點的最左邊的那個mostLeft
//相當於把mostLeft替換掉now
if(now.left!=null&&now.right!=null){
TreeNode mostLeft=now.right;
TreeNode mostLeftParent=now;
while(mostLeft.left!=null){
mostLeftParent=mostLeft;
mostLeft=mostLeft.left;
}
if(mostLeft==now.right){
if(isLeft){
parent.left=now.right;
}
else{
parent.right=now.right;
}
now.right.left=now.left;
}
else{
mostLeftParent.left=mostLeft.right;
if(isLeft){
parent.left=mostLeft;
}
else{
parent.right=mostLeft;
}
mostLeft.right=now.right;
mostLeft.left=now.left;
}
}
}
如果是root,說明它沒有parent,需要特殊處理,但是邏輯與上面的相同,只是沒有parent的操作
//如果now是root,說明它沒有parent,需要特殊處理,但是邏輯與下面的相同,只是沒有parent的操作
if(now==root){
if(now.left==null&&now.right==null){
root=null;
return;
}
if(now.left!=null&&now.right==null){
root=now.left;
return;
}
if(now.left==null&&now.right!=null){
root=now.right;
return;
}
if(now.left!=null&&now.right!=null){
TreeNode mostLeft=now.right;
TreeNode mostLeftParent=now;
while(mostLeft.left!=null){
mostLeftParent=mostLeft;
mostLeft=mostLeft.left;
}
if(mostLeft==now.right){
now.right.left=root.left;
root=now.right;
return;
}
else{
mostLeftParent.left=mostLeft.right;
root=mostLeft;
mostLeft.right=now.right;
mostLeft.left=now.left;
return;
}
}
}
應用
搜尋
只要o(logn)時間即可
排序
中序遍歷即可,o(n)時間
完整程式碼
package datastructure.tree.binarysearchtree;
public class Main {
public static void main(String[] args) {
int[] x=new int[]{1,3,2,-4,0,4};
BinarySearchTree tree=new BinarySearchTree(x);
//tree.insertNode(3);
BinarySearchTree.printTree(tree.root);
//BinarySearchTree.inOrder(tree.root);
//BinarySearchTree.breadthFirstSearch(tree.root);
//BinarySearchTree.depthFirstSearch(tree.root);
//TreeNode now=tree.findNode(2);
//now.printTree(now);
tree.deleteNode(1);
BinarySearchTree.printTree(tree.root);
}
}
package datastructure.tree.binarysearchtree;
import java.util.LinkedList;
import java.util.Queue;
public class TreeNode {
int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int x) {
val = x;
left=null;
right=null;
}
//通過佇列 當前下層節點個數 列印每層的節點和null,為了方便列印
public static void printTree(TreeNode root){
if(root == null)
return;
Queue<TreeNode> queue = new LinkedList<TreeNode>();
int current;//當前層 還未列印的結點個數
int next;//下一層結點個數
queue.offer(root);
current = 1;
next = 0;
while(!queue.isEmpty()){
TreeNode currentNode = queue.poll();
if (currentNode!=null) {
System.out.print(currentNode.val+" ");
current--;
}
else{
System.out.print("null ");
current--;
queue.offer(null);
next++;
queue.offer(null);
next++;
if(current ==0){
System.out.println();
current = next;
next = 0;
int temp=0;
for (TreeNode treeNode : queue) {
if(treeNode==null){
temp++;
}
}
if(temp==current){
System.out.println("end");
break;
}
}
continue;
}
if(currentNode.left != null){
queue.offer(currentNode.left);
next++;
}
else{
queue.offer(null);
next++;
}
if(currentNode.right != null){
queue.offer(currentNode.right);
next++;
}
else{
queue.offer(null);
next++;
}
if(current ==0){
System.out.println();
current = next;
next = 0;
int temp=0;
for (TreeNode treeNode : queue) {
if(treeNode==null){
temp++;
}
}
if(temp==current){
System.out.println("end");
break;
}
}
}
}
}
package datastructure.tree.binarysearchtree;
import java.nio.channels.NetworkChannel;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;
public class BinarySearchTree {
public TreeNode root;
//只有一個int,則root為x的節點
public BinarySearchTree(int x){
root=new TreeNode(x);
}
//x為陣列,如果長度為0,初始化root為0的節點
//否則逐個插入x的元素
public BinarySearchTree(int[] x){
int length=x.length;
if(length==0){
root=new TreeNode(0);
return;
}
for(int i=0;i<length;i++){
insertNode(x[i]);
}
}
//插入節點,如果已存在相同的樹,則不插入,返回已有的節點,否則返回插入的節點
//將now=root 將now的值與x比較,相同返回now,x大,則如果now無右節點,建立右節點x並返回,否則now=now.right
//x小則反之亦然
public TreeNode insertNode(int x){
if(root==null){
root=new TreeNode(x);
return root;
}
TreeNode now=root;
while(true){
if(now.val==x){
return now;
}
if(now.val>x){
if(now.left==null){
now.left=new TreeNode(x);
return now.left;
}
else{
now=now.left;
continue;
}
}
else{
if(now.right==null){
now.right=new TreeNode(x);
return now.right;
}
else{
now=now.right;
continue;
}
}
}
}
//根據int x查詢二叉搜尋樹的節點,如果沒找到 返回null,找到了,返回該節點
//now=root 進入true的迴圈 如果now=null,說明沒有返回null,now的值=x,說明找到,返回now
//否則根據now與x的大小,now=now的左右子節點
public TreeNode findNode(int x){
if(root==null){
return null;
}
TreeNode now=root;
while(true){
if(now==null){
return null;
}
if(now.val==x){
return now;
}
if(now.val<x){
now=now.right;
}
else {
now=now.left;
}
}
}
//如果樹中沒有值為x,則啥都不幹,否則刪除這個節點
//先找到這個x,與查詢的方法不同,還要找到它的父節點
//如果now是root,說明它沒有parent,需要特殊處理,但是邏輯與下面的相同,只是沒有parent的操作
//如果now沒有左右子節點,則parent的子節點為null,根據isLeft判斷now是parent的左右節點
//如果now只有左節點,則根據isleft parent的左右節點為now.left
//如果now只有右節點,則根據isleft parent的左右節點為now.right
public void deleteNode(int x){
if(root==null){
return;
}
TreeNode now=root;
TreeNode parent=root;
boolean isLeft=true;
while(true){
if(now==null){
return;
}
if(now.val==x){
break;
}
if(now.val<x){
parent=now;
now=now.right;
isLeft=false;
}
else {
parent=now;
now=now.left;
isLeft=true;
}
}
//如果now是root,說明它沒有parent,需要特殊處理,但是邏輯與下面的相同,只是沒有parent的操作
if(now==root){
if(now.left==null&&now.right==null){
root=null;
return;
}
if(now.left!=null&&now.right==null){
root=now.left;
return;
}
if(now.left==null&&now.right!=null){
root=now.right;
return;
}
if(now.left!=null&&now.right!=null){
TreeNode mostLeft=now.right;
TreeNode mostLeftParent=now;
while(mostLeft.left!=null){
mostLeftParent=mostLeft;
mostLeft=mostLeft.left;
}
if(mostLeft==now.right){
now.right.left=root.left;
root=now.right;
return;
}
else{
mostLeftParent.left=mostLeft.right;
root=mostLeft;
mostLeft.right=now.right;
mostLeft.left=now.left;
return;
}
}
}
//如果now沒有左右子節點,則parent的子節點為null,根據isLeft判斷now是parent的左右節點
if(now.left==null&&now.right==null){
if(isLeft){
parent.left=null;
return;
}
else{
parent.right=null;
return;
}
}
//如果now只有左節點,則根據isleft parent的左右節點為now.left
if(now.left!=null&&now.right==null){
if(isLeft){
parent.left=now.left;
return;
}
else{
parent.right=now.left;
return;
}
}
//如果now只有右節點,則根據isleft parent的左右節點為now.right
if(now.left==null&&now.right!=null){
if(isLeft){
parent.left=now.right;
return;
}
else{
parent.right=now.right;
return;
}
}
//如果now左右節點都有,則根據isleft parent的左右節點為now的右節點的最左邊的那個mostLeft
//相當於把mostLeft替換掉now
if(now.left!=null&&now.right!=null){
TreeNode mostLeft=now.right;
TreeNode mostLeftParent=now;
while(mostLeft.left!=null){
mostLeftParent=mostLeft;
mostLeft=mostLeft.left;
}
if(mostLeft==now.right){
if(isLeft){
parent.left=now.right;
}
else{
parent.right=now.right;
}
now.right.left=now.left;
}
else{
mostLeftParent.left=mostLeft.right;
if(isLeft){
parent.left=mostLeft;
}
else{
parent.right=mostLeft;
}
mostLeft.right=now.right;
mostLeft.left=now.left;
}
}
}
//遞迴
public static void preOrder(TreeNode root){
if(root==null){
return;
}
System.out.print(root.val+" ");
preOrder(root.left);
preOrder(root.right);
}
public static void inOrder(TreeNode root){
if(root==null){
return;
}
inOrder(root.left);
System.out.print(root.val+" ");
inOrder(root.right);
}
public static void postOrder(TreeNode root){
if(root==null){
return;
}
postOrder(root.left);
postOrder(root.right);
System.out.print(root.val+" ");
}
//通過佇列 當前下層節點個數 列印每層的節點和null
public static void printTree(TreeNode root){
if(root == null)
return;
Queue<TreeNode> queue = new LinkedList<TreeNode>();
int current;//當前層 還未列印的結點個數
int next;//下一層結點個數
queue.offer(root);
current = 1;
next = 0;
while(!queue.isEmpty()){
TreeNode currentNode = queue.poll();
if (currentNode!=null) {
System.out.print(currentNode.val+" ");
current--;
}
else{
System.out.print("null ");
current--;
queue.offer(null);
next++;
queue.offer(null);
next++;
if(current ==0){
System.out.println();
current = next;
next = 0;
int temp=0;
for (TreeNode treeNode : queue) {
if(treeNode==null){
temp++;
}
}
if(temp==current){
System.out.println("end");
break;
}
}
continue;
}
if(currentNode.left != null){
queue.offer(currentNode.left);
next++;
}
else{
queue.offer(null);
next++;
}
if(currentNode.right != null){
queue.offer(currentNode.right);
next++;
}
else{
queue.offer(null);
next++;
}
if(current ==0){
System.out.println();
current = next;
next = 0;
int temp=0;
for (TreeNode treeNode : queue) {
if(treeNode==null){
temp++;
}
}
if(temp==current){
System.out.println("end");
break;
}
}
}
}
//寬度優先遍歷 是簡化的按層遍歷,沒有了current和next和列印null
public static void breadthFirstSearch(TreeNode root){
Queue<TreeNode> queue = new LinkedList<TreeNode>();
if(root==null){
return;
}
queue.offer(root);
while(!queue.isEmpty()){
TreeNode now=queue.poll();
System.out.print(now.val+" ");
if(now.left!=null){
queue.offer(now.left);
}
if(now.right!=null){
queue.offer(now.right);
}
}
System.out.println();
}
//深度優先遍歷
//用棧 彈出自身 先加入右節點 再加入左節點,這樣先彈出左節點,左節點的左右子節點又塞進去,在原右節點上面
public static void depthFirstSearch(TreeNode root){
Stack<TreeNode> stack=new Stack<TreeNode>();
if(root==null){
return;
}
stack.push(root);
while(!stack.isEmpty()){
TreeNode now=stack.pop();
System.out.print(now.val+" ");
if(now.right!=null){
stack.push(now.right);
}
if(now.left!=null){
stack.push(now.left);
}
}
System.out.println();
}
}