JAVA 实现 - 二叉搜索树
二叉搜索树
二叉搜索树/二叉查找树/二叉排序树
特点:
- 树节点增加key属性,用来比较谁大谁小,key不可以重复
- 对于任意一个树节点,它的key比左子树的key都大,同时也比右子树的key都大
/**
* 二叉搜索树
*/
public class BSTree1 {
TreeNode root;
static class TreeNode {
int key; //节点的健
Object value; //节点的值
TreeNode left;
TreeNode right;
public TreeNode(int key) {
this.key = key;
}
public TreeNode(int key, Object value) {
this.key = key;
this.value = value;
}
public TreeNode(int key, Object value, TreeNode left, TreeNode right) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
}
}
查找元素 - 递归实现
public class BSTree1 {
...
public Object get(int key) {
return doGet(root, key);
}
private Object doGet(TreeNode node, int key) {
if (node == null) { //没找到结束递归
return null;
}
if (key < node.key) { //向左找
return doGet(node.left, key);
} else if (key > node.key) { //向右找
return doGet(node.right, key);
} else { //找到了
return node.value;
}
}
}
查找元素 - 非递归实现
public class BSTree1 {
...
public Object get(int key){
TreeNode node = root;
while(node != null){
if (key < node.key){ //向左
node = node.left;
}else if (key > node.key){ //向右
node = node.right;
}else{
return node.value;
}
}
return null;
}
}
获取最小健上的值
非递归实现:
package com.datastructure.binarytree.bstree;
/**
* 二叉搜索树
*/
public class BSTree1 {
...
public Object getMin() {
if (this.root == null) {
return null;
}
TreeNode node = this.root;
while (node.left != null) {
node = node.left;
}
return node.value;
}
}
递归实现:
package com.datastructure.binarytree.bstree;
/**
* 二叉搜索树
*/
public class BSTree1 {
...
/**
* 递归实现 - 查找最小健对应的值
*/
public Object getMin(){
return doGetMin(root);
}
private Object doGetMin(TreeNode node){
if (node == null){
return null;
}
if(node.left != null){
return doGetMin(node.left);
}else {
return node.value;
}
}
插入节点
思路:
1.利用get的思路,找到值时更新
2.找不到节点时插入元素,关键是找到父节点。
package com.datastructure.binarytree.bstree;
/**
* 二叉搜索树
*/
public class BSTree1 {
...
public void put(int key, Object value) {
TreeNode node = root;
TreeNode parent = null;
while (node != null) {
parent = node; //node 作为一个指针,记录了父节点
if (key < node.key) {
node = node.left;
} else if (key > node.key) {
node = node.right;
} else {
//1. 找到 key 值, 更新操作
node.value = value;
return;
}
}
//2. 没有找到,创建一个新节点插入
if (parent == null) {
root = new TreeNode(key,value);
return;
}
if (key < parent.key) { //作为左孩子
parent.left = new TreeNode(key,value);
} else { //作为右孩子
parent.right = new TreeNode(key,value);
}
}
}
获取节点的前驱
package com.datastructure.binarytree.bstree;
import com.sun.source.tree.Tree;
/**
* 二叉搜索树
*/
public class BSTree1 {
...
/**
* 查找节点的前驱节点
* 情况1: 节点有左子树,此时前任就是左子树的最大值
* 情况2: 节点没有左子树,若离它最近的,自左而来的祖先就是前任
*
* @param key
* @return
*/
public Object predecessor(int key) {
TreeNode p = this.root;
TreeNode accessorFromLeft = null; //记录自左而来的组祖先
while (p != null) {
if (key < p.key) {
p = p.left;
} else if (p.key < key) {
accessorFromLeft = p; //记录自左而来的祖先
p = p.right;
} else {
break;
}
}
if (p == null) { // 没有找到 key
return null;
}
if (p.left != null) { //情况1: 节点有左子树,此时前任就是左子树的最大值
return getMax(p); //获取左子树的最大值
}
//情况2: 节点没有左子树,若离它最近的,自左而来的祖先就是前任
return accessorFromLeft != null ? accessorFromLeft.value : null;
}
}
查找节点的后继节点
package com.datastructure.binarytree.bstree;
import com.sun.source.tree.Tree;
/**
* 二叉搜索树
*/
public class BSTree1 {
...
/**
* 查找节点的后继节点:
* 情况1: 节点有右子树,右子树的最小值即为该节点的后继节点
* 情况2: 节点没有右子树,离它最近的自右而来的节点即为该节点的后继节点
*/
public Object sucessor(int key) {
TreeNode p = root;
TreeNode accessorFromRight = null; //记录自右而来的祖先节点
while (p != null) {
if (key < p.key) {
accessorFromRight = p;
p = p.left;
} else if (p.key < key) {
p = p.right;
} else {
break; // 找到
}
}
if (p == null) { //没找到key
return null;
}
if (p.right != null) { //情况1: 节点有右子树,右子树的最小值即为该节点的后继节点
return getMin(p);
}
//情况2: 节点没有右子树,离它最近的自右而来的节点即为该节点的后继节点
return accessorFromRight != null ? accessorFromRight.value : null;
}
}
删除节点
- 删除节点没有左孩子,将右孩子托孤给Parent
- 删除节点没有右孩子,将左孩子托孤给Parent
- 删除节点左右孩子都没有,已经被涵盖在情况1、2 当中,把 null 托孤给Parent
- 删除节点左右孩子都有,可以将它的后继节点(称为S)托孤给Parent,再称S的父节点为SP,又分为两种情况
4.1 SP就是删除节点,此时D(删除节点)与S紧邻,只需将S托孤给Parent
4.2 SP不是被删除节点,此时D与S不相邻,此时需要将S的后代托孤给SP,再将S 托孤给Parent.
package com.datastructure.binarytree.bstree;
import com.sun.source.tree.Tree;
/**
* 二叉搜索树
*/
public class BSTree1 {
...
public Object delete(int key) {
TreeNode p = root;
TreeNode parent = null;
while (p != null) {
if (key < p.key) {
parent = p;
p = p.left;
} else if (p.key < key) {
parent = p;
p = p.right;
} else {
break; // 找到
}
}
if(p == null){
return null;
}
//情况1:只有左孩子没有右孩子
if(p.left != null && p.right == null){
shift(parent ,p, p.left);
} else if(p.left == null && p.right != null){ //情况2:只有右孩子没有左孩子
shift(parent, p, p.right);
}else if(p.left == null && p.right ==null){ // 情况3:删除节点为叶子节点
shift(parent, p , null);
} else{
//情况4:
//4.1 寻找后继节点s
TreeNode s = p.right;
TreeNode sParent = null;
while (s.left != null){
sParent = s;
s = s.left;
}
// 4.11 后继点解与被删节点不相邻
if(sParent != p){
//处理后继的后事
shift(sParent, s, s.right);
s.right = p.right;
}
//4.12 后继节点与被删除节点相邻
//4.2 处理后继的事儿
shift(parent,p,s);
//4.3 后继取代删除节点
s.left = p.left;
}
return p.value;
}
/**
* 执行删除操作
*/
private void shift(TreeNode parent,TreeNode deleted, TreeNode child){
if(parent == null){ //删除节点为根节点
root = child;
}else if(parent.right == deleted){ //被删除节点为父节点的右节点
parent.right = child;
}else{
parent.left = child; //被删除节点为父节点的左节点
}
}
}
范围查询
package com.datastructure.binarytree.bstree;
import com.sun.source.tree.Tree;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* 二叉搜索树
*/
public class BSTree1 {
...
/*
查找二叉搜索树比key小的所有value
思路:采用中序遍历,中序遍历的结果是一个升序
*/
public List<Object> less(int key) {
List<Object> result = new ArrayList<>();
LinkedList<TreeNode> stack = new LinkedList<>();
TreeNode curr= root;
while (curr!= null || !stack.isEmpty()) {
if (curr!= null) {
stack.push(curr);
curr= curr.left;
} else { //往回走
TreeNode pop = stack.pop();
//处理值
if(pop.key < key){
result.add(pop.value);
}else{
break; // 因为是中序遍历是升级,一旦发现大于的就可以结束循环
}
curr= pop.right;
}
}
return result;
}
/*
查找二叉树搜索树比key大的所有value
思路:如果采用中序遍历查找的速度慢,因为中序遍历的结果是升序,
可以使用 反向中序遍历: 右值左
*/
public List<Object> greater(int key) {
List<Object> result = new ArrayList<>();
LinkedList<TreeNode> stack = new LinkedList<>();
TreeNode curr= root;
while (curr!= null || !stack.isEmpty()) {
if (curr!= null) {
stack.push(curr);
curr= curr.right;
} else { //往回走
TreeNode pop = stack.pop();
//处理值
if(pop.key > key){
result.add(pop.value);
}
curr= pop.left;
}
}
return result;
}
/*
查找 >= key1 并且 <= key2 的所有value
*/
public List<Object> between(int key1, int key2){
List<Object> result = new ArrayList<>();
LinkedList<TreeNode> stack = new LinkedList<>();
TreeNode curr= root;
while (curr!= null || !stack.isEmpty()) {
if (curr!= null) {
stack.push(curr);
curr= curr.left;
} else { //往回走
TreeNode pop = stack.pop();
//处理值
if(pop.key >= key1 && pop.key <= key2){
result.add(pop.value);
} else if (pop.key > key2) {
break;
}
curr= pop.right;
}
}
return result;
}
}
判断否是一个合法的二叉搜索树
解法1:运用中序遍历,如果发现前面的节点值比当前节点值大,返回false
class Solution {
public boolean isValidBST(TreeNode node) {
TreeNode curr = node;
LinkedList<TreeNode> stack = new LinkedList<>();
long prev = Long.MIN_VALUE;
while (curr != null || !stack.isEmpty()) {
if (curr != null) {
stack.push(curr);
curr = curr.left;
} else {
TreeNode pop = stack.pop();
// 处理值
if (prev >= pop.val) {
return false;
}
prev = pop.val;
curr = pop.right;
}
}
return true;
}
}
范围求和
package com.datastructure.binarytree;
import java.util.LinkedList;
/**
* 范围求和: 给定二叉搜索树的根节点root,返回位于 范围[low,high] 之间所有节点的值的和
*/
public class E05Leetcode938 {
//解法1:中序遍历非递归实现,运行耗时 1ms
public int rangeSumBST(TreeNode root, int low, int hight) {
LinkedList<TreeNode> stack = new LinkedList<>();
int sum = 0;
TreeNode curr = root;
while (curr != null || !stack.isEmpty()) {
if (curr != null) {
stack.push(curr);
curr = curr.left;
} else {
TreeNode pop = stack.pop();
//处理值
if (pop.val >= hight) {
break;
}
if (pop.val >= low) {
sum += pop.val;
}
curr = pop.right;
}
}
return sum;
}
//解法2:递归求解,运行耗时: 0 ms
public int rangeSumBST2(TreeNode node, int low, int hight) {
if (node == null) {
return 0;
}
//情况1: 当前节点小于下限,左子树不再考虑,递归求解右子树累加的结果
if (node.val < low) {
return rangeSumBST(node.right, low, hight);
}
//情况2:当前节点大于上限
if (node.val > hight) { //不再考虑右子树,返回左子树的累加结果
return rangeSumBST(node.left, low, hight);
}
// 在[low,hight]
return node.val + rangeSumBST(node.right, low, hight) + rangeSumBST(node.left, low, hight);
}
}
前序遍历构造二叉树
解法1:
package com.datastructure.binarytree;
/**
* 根据前序遍历构造二叉树
* 题目说明:
* 1.preorder 长度 >= 1
* 2.preorder 没有重复值
*/
public class E06Leetcode1008 {
/*
8,5,1,7,10,12
8
/ \
5 10
/ \ \
1 7 12
*/
public TreeNode bstFromPreorder(int[] preorder) {
TreeNode root = new TreeNode(preorder[0]);
for (int i = 1; i < preorder.length; i++) {
insert(root, preorder[i]);
}
return root;
}
private TreeNode insert(TreeNode node, int val) { // 5, 1
if (node == null) { //找到空位
return new TreeNode(val);
}
// node 不为空
if (val < node.val) {
node.left = insert(node.left, val); //建立父子关系
} else if (node.val < val) {
node.right = insert(node.right, val); //建立父子关系
}
return node;
}
}
解法2:
package com.datastructure.binarytree;
/**
* 根据前序遍历构造二叉树
* 题目说明:
* 1.preorder 长度 >= 1
* 2.preorder 没有重复值
*/
public class E06Leetcode1008 {
/**
* 解法2:分治法
* 8,5,1,7,10,12
* 根 8, 左子树,5,1,7,右子树10,12
*/
public TreeNode bstFromPreorder(int[] preorder) {
return partition(preorder, 0, preorder.length - 1);
}
private TreeNode partition(int[] preorder, int start, int end) {
if (start > end) {
return null;
}
TreeNode node = new TreeNode(preorder[start]);
//寻找比根节点大的第一个索引
int index = start + 1;
while (index <= end) {
if (preorder[index] > preorder[start]) {
break;
}
index++;
}
//找到比根节点大的第一个索引,递归
node.left = partition(preorder, start + 1, index - 1);
node.right = partition(preorder, index, end);
return node;
}
}
本文来自博客园,作者:chuangzhou,转载请注明原文链接:https://www.cnblogs.com/czzz/p/17936123.html