目录
二叉搜索树的概念
二叉搜索树(二叉排序树):
1.首先是一棵二叉树
2.其次左子树上所有节点的值都小于根节点的值,右子树上所有节点的值都大于根节点的值。
图解:
实现二叉搜索树的添加方法
先讲解思路,再进行图解,最后实现代码
明确一个点:添加的位置是在叶子结点上
思路:
1.树为空,直接添加
2.树不为空,那就要判断是放在左子树的位置还是右子树的位置,接着循环到叶子节点进行添加
图解 :
代码实现:
public boolean insert(int val){
//1.判断是否为空
if(root == null){
root = new TreeNode(val);
return true;
}
//2.cur指向当前节点
//preCur指向cur的上一个节点
TreeNode preCur = root;
TreeNode cur = null;
//3.当cur超出叶子节点后,那此时preCur就指向要插入的叶子节点
while(preCur != null){
//判断往左插入还是往右插入
if(preCur.val < val){
cur = preCur;//保留上一步
preCur = preCur.right;//往右走
}
else if(preCur.val > val){
cur = preCur;//保留上一步
preCur = preCur.left;//往左走
}
else{
return false;//二叉搜索树中没有相等的值,如果相等就return false
}
}
//4.while循环结束此时curPre成为了叶子结点,tmpNext已经成为了null
//5.看看在比较进行插入
if(cur.val < val){
cur.right = new TreeNode(val);
}else{
cur.left = new TreeNode(val);
}
return true;
}
实现二叉搜索树的查找方法
先讲解思路,再进行图解,最后实现代码
思路:搜索的话其实在添加中已经实现过了,来走一遍
1.先判断二叉树是否为空,如果为空直接false
2.不为空,就从跟节点不断去比较大小,如果找到了就返回true,如果cur引用(指向当前节点)不断比较,超过了叶子节点(也就是cur指向空),那就说明没找到,返回false。
代码实现:
public boolean search(int val){
//1.判断是否为空
if(root == null){
return false;
}
//2.cur从根节点开始不断指向当前要比较的节点
TreeNode cur = root;
//3.cur不为空时,也说明还在寻找
while(cur != null){
//与当前节点进行比较
if(cur.val < val){//val值大,cur就往右边走
cur = cur.right;
}
else if(cur.val > val){//val值小,cur就往左边走
cur = cur.left;
}
else//值相同,就返回true
{
return true;
}
}
//能从while跳出来就说明cur此时为空了,也就没找到
return false;
}
实现二叉搜索树的删除方法
先讲解思路,再进行图解,最后实现代码
思路:删除比较麻烦一点,要分情况。
1.树为空,无法删除,返回false
2.先遍历寻找到要删除的节点
3.分情况删除节点
a.1删除节点的左子树为null
a.1.1删除节点为根节点
a.1.2删除节点为根节点的右子树
a.1.3删除节点为根节点的左子树
a.2删除节点的右子树为null
a.2.1删除节点为根节点
a.2.2删除节点为根节点的右子树
a.2.3删除节点为根节点的左子树
a.3删除节点的左右子树都不为null
此时删除解节点的左右子树都不为空,我们需要采取一种替换法的方式
简单来说:
1.就是在被删节点的右子树中找最小值,通过最小值来覆盖要删除的节点,接着删除右子树中的最小值即可。
2.同理,也可以在被删节点的左子树中找最大值,通过最大值来覆盖要删除的节点,接着删除左子树中的最大值即可。
我的实现:我选择的是第1中方式,在右子树中找最小值。
图解替换法:
a.1图解
a.2图解
a.3替换法代码
public boolean remove(int val){
//1.树为空,直接return false
if(root == null){
return false;
}
//2.先循环找到要删除的元素
TreeNode cur = root;
TreeNode curPre = null;
while(cur != null){
if(cur.val < val){
//右侧找
curPre = cur;
cur = cur.right;
}else if(cur.val > val){
//左侧找
curPre = cur;
cur = cur.left;
}else{
//找到了,删除节点
removeNode(curPre,cur);
return true;
}
}
return false;//没找到该节点
}
private void removeNode(TreeNode parent,TreeNode tmp){//parent就是curPre tmp就是cur
//3.分情况
// a.1被删节点的左子树为null
if(tmp.left == null){
//a.1.1删除节点为根节点
if(tmp == root){
parent = tmp.right;
//a.1.2删除节点为根节点的右子树
}else if(parent.right == tmp){
parent.right = tmp.right;
//a.1.3删除节点为根节点的左子树
}else{
parent.left = tmp.right;
}
//a.2别删节点的右侧为null
}else if(tmp.right == null){
//a.2.1删除节点为根节点
if(tmp == root){
parent = tmp.left;
//a.1.2删除节点为根节点的右子树
}else if(parent.right == tmp){
parent.right = tmp.left;
//a.1.3删除节点为根节点的左子树
}else{
parent.left = tmp.left;
}
//a.3删除节点的左右子树都不为null,我使用被删节点的右子树中找最小值
}else{
TreeNode curParent = tmp;//记录cur的前一个
TreeNode cur = tmp.right;//右子树找Min
//被删节点的右子树下的左子树如果存在,那最小值肯定就在左子树
while(cur.left != null){
curParent = cur;
cur = cur.left;
}
//此时有两种情况,一种压根没进while(无左子树),一种是进了(有左子树)
//找到最小值并覆盖被删节点的值
tmp.val = cur.val;
//连接跳过最小值
if(cur == curParent.left) {//有左子树
curParent.left = cur.right;//此时cur是最小,连接cur的右子树
}else {//无左子树
curParent.right = cur.right;
}
}
}
附带java代码
/**
* 二叉搜索树:左子树的值都小于根节点的值,右子树的值都大于根节点的值
* 平衡二叉树(AVL): 左子树和右子树的最大高度差小于或等于1,并且是一颗二叉搜索树
*/
/**
* 实现二叉搜索树的基本功能:添加,删除,搜索
*/
class TreeNode{
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val) {
this.val = val;
}
}
public class BinarySearchTreeTest {
public TreeNode root;
private void removeNode(TreeNode parent,TreeNode tmp){//parent就是curPre tmp就是cur
//3.分情况
// a.1被删节点的左子树为null
if(tmp.left == null){
//a.1.1删除节点为根节点
if(tmp == root){
parent = tmp.right;
//a.1.2删除节点为根节点的右子树
}else if(parent.right == tmp){
parent.right = tmp.right;
//a.1.3删除节点为根节点的左子树
}else{
parent.left = tmp.right;
}
//a.2别删节点的右侧为null
}else if(tmp.right == null){
//a.2.1删除节点为根节点
if(tmp == root){
parent = tmp.left;
//a.1.2删除节点为根节点的右子树
}else if(parent.right == tmp){
parent.right = tmp.left;
//a.1.3删除节点为根节点的左子树
}else{
parent.left = tmp.left;
}
//a.3删除节点的左右子树都不为null,我使用被删节点的右子树中找最小值
}else{
TreeNode curParent = tmp;//记录cur的前一个
TreeNode cur = tmp.right;//右子树找Min
//被删节点的右子树下的左子树如果存在,那最小值肯定就在左子树
while(cur.left != null){
curParent = cur;
cur = cur.left;
}
//此时有两种情况,一种压根没进while(无左子树),一种是进了(有左子树)
//找到最小值并覆盖被删节点的值
tmp.val = cur.val;
//连接跳过最小值
if(cur == curParent.left) {//有左子树
curParent.left = cur.right;//此时cur是最小,连接cur的右子树
}else {//无左子树
curParent.right = cur.right;
}
}
}
/**
* 使用替换法找到替换节点
* @param val
*/
public boolean remove(int val){
//1.树为空,直接return false
if(root == null){
return false;
}
//2.先循环找到要删除的元素
TreeNode cur = root;
TreeNode curPre = null;
while(cur != null){
if(cur.val < val){
//右侧找
curPre = cur;
cur = cur.right;
}else if(cur.val > val){
//左侧找
curPre = cur;
cur = cur.left;
}else{
//找到了,删除节点
removeNode(curPre,cur);
return true;
}
}
return false;//没找到该节点
}
//查找
public boolean search(int val){
//1.判断是否为空
if(root == null){
return false;
}
//2.cur从根节点开始不断指向当前要比较的节点
TreeNode cur = root;
//3.cur不为空时,也说明还在寻找
while(cur != null){
//与当前节点进行比较
if(cur.val < val){//val值大,cur就往右边走
cur = cur.right;
}
else if(cur.val > val){//val值小,cur就往左边走
cur = cur.left;
}
else//值相同,就返回true
{
return true;
}
}
//能从while跳出来就说明cur此时为空了,也就没找到
return false;
}
//添加方法
public boolean insert(int val){
//1.判断是否为空
if(root == null){
root = new TreeNode(val);
return true;
}
//2.cur指向当前节点
//preCur指向cur的上一个节点
TreeNode preCur = root;
TreeNode cur = null;
//3.当cur超出叶子节点后,那此时preCur就指向要插入的叶子节点
while(preCur != null){
//判断往左插入还是往右插入
if(preCur.val < val){
cur = preCur;//保留上一步
preCur = preCur.right;//往右走
}
else if(preCur.val > val){
cur = preCur;//保留上一步
preCur = preCur.left;//往左走
}
else{
return false;//二叉搜索树中没有相等的值,如果相等就return false
}
}
//4.while循环结束此时curPre成为了叶子结点,tmpNext已经成为了null
//5.看看在比较进行插入
if(cur.val < val){
cur.right = new TreeNode(val);
}else{
cur.left = new TreeNode(val);
}
return true;
}
//先序遍历
public void preOrder(TreeNode root) {
if(root == null) return;
System.out.print(root.val+" ");
preOrder(root.left);
preOrder(root.right);
}
//中序遍历
public void inOrder(TreeNode root){
if(root == null)
return;
inOrder(root.left);
System.out.print(root.val+" ");
inOrder(root.right);
}
public static void main(String[] args) {
BinarySearchTreeTest testBinarySearchTree = new BinarySearchTreeTest();
testBinarySearchTree.insert(13);
testBinarySearchTree.insert(4);
testBinarySearchTree.insert(17);
testBinarySearchTree.insert(5);
testBinarySearchTree.inOrder(testBinarySearchTree.root);
System.out.println();
testBinarySearchTree.preOrder(testBinarySearchTree.root);
System.out.println();
System.out.println(testBinarySearchTree.search(1));
System.out.println(testBinarySearchTree.search(4));
System.out.println(testBinarySearchTree.search(14));
System.out.println(testBinarySearchTree.search(17));
System.out.println("==========================================");
testBinarySearchTree.remove(13);
testBinarySearchTree.inOrder(testBinarySearchTree.root);
System.out.println();
testBinarySearchTree.preOrder(testBinarySearchTree.root);
}
}