目录
二叉搜索树的概念
二叉搜索树(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();
}
}