二叉搜索树
- 无序树的查找,就是整个遍历,所以时间复杂度为O(N)。
- 为了优化无序查找的时间复杂度,我们把树进行排序,这里引入了二叉搜索树。
二叉搜索树是一个有序树,即左节点的值比根节点的值小,右节点的值比根节点的值大
- 二叉搜索树的时间复杂度为O(logN)
因为每次查找都会减少剩余数据一半的数据量
搜索
- 整体思路
1、定义一个TreeNode,其中包含该节点的value值,以及左右孩子
2、如果 搜索值 = 节点值,返回;
3、如果 搜索值 > 节点值,在右孩子处查找
4、如果 搜索值 < 节点值,在左孩子处查找 - 代码截图(完整代码在【代码实现】模块可以查看到)
删除节点
- 思路:
-
1、找到目标节点
-
2、找到目标节点的父节点
-
3、删除的节点是叶子节点
(1)父节点为空
(2)父节点不为空:判断目标节点是左孩子还是右孩子,
(3)找到目标节点,将赋值为null
-
4、删除的节点有两个子树
(1)找到目标节点左子树的最大值(或 右子树的最小值)
(2)需要先删除最大值,然后将最大值赋值给目标节点赋值:如果不先删除,赋值成功后会有两个最大值
-
5、删除的节点只有一个子树
(1)删除的节点是根节点,且只有左子树:直接将左子树赋给根节点
(2)删除的节点是根节点,且只有右子树:直接将右子树赋给根节点
(3)删除的节点为父节点的左子树,且节点只有左子树:节点的左子树赋值给父节点的左子树
(4)删除的节点为父节点的左子树,且节点只有右子树:节点的右子树赋值给父节点的左子树
(5)删除的节点为父节点的右子树,且节点只有左子树:节点的左子树赋值给父节点的右子树
(6)删除的节点为父节点的右子树,且节点只有右子树:节点的右子树赋值给父节点的右子树
-
二叉树的遍历
-
前序遍历:根 - 左 - 右,借助递归方法传入左节点或右节点
-
中序遍历:左 - 根 - 右,借助递归方法传入左节点或右节点
-
后续遍历:左 - 右 - 根,借助递归方法传入左节点或右节点
-
层序(广度)遍历:借助队列,根节点出队时,将左右孩子入队的思想
代码实现
- TreeNode类
public class TreeNode { private int value; // 节点的值 private TreeNode left; // 左节点 private TreeNode right; // 右节点 // 构造函数 public TreeNode(int value) {this.value = value;} // get 和 set 方法 public int getValue() {return value;} public void setValue(int value) {this.value = value;} public TreeNode getLeft() {return left;} public void setLeft(TreeNode left) {this.left = left;} public TreeNode getRight() {return right;} public void setRight(TreeNode right) {this.right = right;} }
- BinaryTree类:搜索、删除、遍历于一体
import java.util.LinkedList; import java.util.Queue; public class BinaryTree { public TreeNode root; // 定义一个二叉树的根节点 /*** * 普通插入法: * 1、根节点为空,直接赋值,直接返回; * 2、根节点不为空,开始遍历, */ public void insert(int value){ // 创建一个新的节点 TreeNode newNode = new TreeNode(value); // 先来判断root节点是不是为空 if(root==null){ // root为空,直接赋值,直接返回 root = newNode; return; } // root节点不为空,所以需要开始遍历了 TreeNode index = root; // 定义个父节点 TreeNode pre = null; while(true){ pre = index; if(value>=index.getValue()){ // 如果插入的值比较大,就一直往右走 index = index.getRight(); if(index == null){ pre.setRight(newNode); return; } }else { index = index.getLeft(); // 如果插入的值比较大,就一直往右走 if(index==null){ pre.setLeft(newNode); return; } } } } // 【递归插入法】 public void insertDG(TreeNode node,int value){ TreeNode newNode = new TreeNode(value); if(root==null){ root = newNode; return; } // root不为空 if(node.getValue()>=value){ // 判断当前节点的左节点是否为空 if(node.getLeft()==null){ node.setLeft(newNode); return; } // 左节点不为空,需要递归 insertDG(node.getLeft(),value); }else{ // 存入的值大,需要往右边遍历 // 先判断当前节点的右节点是否为空 if(node.getRight()==null){ node.setRight(newNode); return; } // 右节点不为空,需要递归 insertDG(node.getRight(),value); } } /*** * 【前序遍历:父 - 左 - 右】 * @param node */ public void beforOrder(TreeNode node){ // 需要遍历的节点为空,直接返回 if(node==null){return;} // 节点不为空 System.out.print(node.getValue()+" "); // 递归遍历左子树 beforOrder(node.getLeft()); // 递归遍历右子树 beforOrder(node.getRight()); } /*** * 【中序遍历:左 - 父 - 右】 * @param node */ public void inOrder(TreeNode node){ // 递归出口 if(node == null){return;} // 递归遍历左子树 inOrder(node.getLeft()); System.out.print(node.getValue()+" "); // 递归遍历右子树 inOrder(node.getRight()); } /*** * 【后序遍历:左 - 右 -父】 * @param node */ public void afterOrder(TreeNode node){ // 递归出口 if(node==null){return;} // 递归遍历左子树 afterOrder(node.getLeft()); // 递归遍历右子树 afterOrder(node.getRight()); System.out.print(node.getValue()+" "); } /*** * 广度遍历 * @param node */ public void deepOrder(TreeNode node){ Queue<TreeNode> queue = new LinkedList<>(); // 定义一个队列 TreeNode index = null; // 记录从队列中取出的节点 if(node==null){ System.out.println("树为空"); return; }else{ queue.offer(node); while(!queue.isEmpty()){ // 队列不为空时操作 index = queue.poll(); System.out.print(index.getValue()+" "); if(index.getLeft()!=null){ // 左节点不为空,入队 queue.offer(index.getLeft()); } if(index.getRight()!=null){ // 右节点不为空,入队 queue.offer(index.getRight()); } } } } /*** * [普通方法:查找] * @param value:需要查找的值 * @return:TreeNode */ public TreeNode search(int value){ // 定义一个遍历的节点 TreeNode index = root; // 开始遍历,节点不为空 while(index!=null){ if(index.getValue()==value){ return index; }else if(index.getValue()>value){ index = index.getLeft(); }else{ index = index.getRight(); } } return null; } /*** * [递归方法:查找] * @param node:查找的节点 * @param value:查找的值 * @return:TreeNode节点 */ public TreeNode searchDG(TreeNode node,int value){ // 递归出口 if(node==null){return null; } // 开始递归 if(node.getValue() == value){ return node; }else if(node.getValue()>value){ return searchDG(node.getLeft(),value); }else { return searchDG(node.getLeft(),value); } } // 查找父节点 public TreeNode searchParent(TreeNode node,int value){ // 递归出口 if(node==null){ return null; } // 判断当前节点是不是目标节点的父节点 // 是父节点的条件:左节点不为空且等于value 或者 右节点不为空,且等于value if((node.getLeft()!=null && node.getLeft().getValue()==value) || (node.getRight()!=null && node.getRight().getValue()==value)){ return node; }else if(node.getValue()>value){ // 当前 return searchParent(node.getLeft(),value); }else{ return searchParent(node.getRight(),value); } } // 删除节点 public void delete(int value){ // 如果跟节点为空,直接返回 if(root == null){ System.out.println("空树,无法删除"); return ; } // 根节点不为空: // 找到目标节点 TreeNode target = search(value); if(target==null){ System.out.println("不存在的节点"); return; } // 找到父节点 TreeNode parent = searchParent(root,value); if(target.getLeft()==null && target.getRight()==null){ // 删除叶子节点 // 1、如果父节点为空,那么这个节点就是根节点 if(parent == null){ root = null; return; } // 2、父节点不为空,那么需要判断目标节点是左节点 还是 右节点了 if(parent.getLeft()!=null && parent.getLeft()==target){ // (1)目标节点是父节点的左孩子 parent.setLeft(null); }else{ // (2)目标节点是父节点的右孩子 parent.setRight(null); } }else if(target.getLeft()!=null && target.getLeft()!=null){ // 删除有两个子树的节点 // 找到目标节点左子树的最大值 TreeNode index = target.getLeft(); while(index.getRight()!=null){ index = index.getRight(); } int Max = index.getValue(); // 要先删除后赋值 delete(Max); target.setValue(Max); }else{ // 删除只有一个子树的节点 //没有父节点,即是根节点 if(parent==null){ // 判断是左子树还是右子树 if(target.getLeft()!=null){ // 根节点,只有左子树 root = target.getLeft(); }else{ // 根节点,只有右子树 root = target.getRight(); } return; } // 有父节点,即非根节点 if(parent.getLeft()!=null && parent.getLeft().getValue()==value){ // 删除节点是父节点的左子树。判断要删除的节点的子节点是左子树还是右子树 if(target.getLeft()!=null){ // 要删除的节点的唯一节点只有左子树 parent.setLeft(target.getLeft()); }else{ // 要删除的节点的唯一节点只有右子树 parent.setLeft(target.getRight()); } }else{ // 删除节点是父节点的右子树 if(target.getLeft()!=null){ // 要删除的节点的唯一节点只有左子树 parent.setRight(target.getLeft()); }else{ // 要删除的节点的唯一节点只有右子树 parent.setRight(target.getRight()); } } } } }
- Test类
public class Test { public static void main(String[] args) { BinaryTree forest = new BinaryTree(); forest.insert(10); forest.insertDG(forest.root,15); forest.insert(7); forest.insertDG(forest.root,3); forest.insert(20); forest.insertDG(forest.root,12); forest.insert(9); forest.insertDG(forest.root,25); forest.insert(17); forest.insertDG(forest.root,2); forest.delete(10); System.out.println("前序遍历:"); forest.beforOrder(forest.root); System.out.println("\n广度遍历:"); forest.deepOrder(forest.root); } }