删除:分三种情况处理
a. 如果要删除结点没有孩子,那么直接将该结点删除就行了。
b. 如果要删除结点只有一个孩子,那么需要就父亲结点对应的指针,指向孩子结点。
c. 如果要删除结点有两个孩子,那么我们可以找到这个结点的右子树中最小结点 (或者
左子树中最大结点),把它替换到要删除的结点上,然后再删除掉这个最小结点。
如果搞不懂remove方法,就假设删除结点C,手动执行一下代码
关于层次遍历(用队列来实现):
用栈实现先序遍历:
用栈实现中序遍历:
建树操作:
1.这个方法不依赖于BinarySearchTree类对象而存在,所以得加static
2.但是这样做的话没有泛型消息,那么怎么办呢?我们可以把这个方法设置成泛型方法, 加<T>
3.但是BinarySearchTree中的元素都是可比较的,所以我们要求这个泛型extends Comparable<T>
2.有人说Comparable是接口啊,为啥用extends,extends表示泛型的向下限定,这是固定写法,T extends Comparable表示要求T必须是Comparable或者Comparable的子类类型。
4.T extends Comparable<? super T>既然我能比较T的父类,那我也能比较T
5.你定义了泛型T,但编译器怎么知道T是啥类型呢?T怎么传入进来呢?
返回值类型:BinarySearchTre<T> 、参数:List<T> preOrder,List<T> inOrder
package com.cskaoyan.tree;
import sun.security.krb5.internal.tools.Klist;
import java.util.*;
/**
* @author shihao
* @create 2020-05-20 10:33
* <p>
* API:
* 增:boolean add(E e) 添加成功返回true,否则返回false(二叉排序树中有和e相同的值)
* 删:boolean remove(E e) 删除成功返回true,否则返回false(二叉排序树中无和e相同的值)
* void clear(); 清空树中所有元素
* 查:boolean contains(E e)
* E min()
* E max() 求最大值和最小值
* 遍历:List<E> preOrder();
* List<E> inOrder();
* List<E> postOrder();
* List<E> levelOrder();
* 建树:静态方法,因为建树操作不依赖于BinarySearchTree对象而存在
* static <T> BinarySearchTree buildTree(List<T> preOrder,List<T> inOrder)
* 获取属性:
* boolean isEmpty() 判空
* int size() 结点个数
* int height() 树的高度(二叉排序树的性能是依赖于二叉排序树的高度的)
*/
//我们希望BinarySearchTree结点可以进行比较,在java中除了基本数据类型外,
// 要想实现比较,得实现Comparable接口,而Comparable接口本身就是泛型接口,
// 所以得Comparable<E>,如果不写<E>的话,我们认为它可以对任意元素(Object)进行比较的
//public class BinarySearchTree<E extends Comparable<E>>
//第一个<E>定义泛型,第二个<E>使用泛型。但这样的话只能对E类型的元素进行比较
//但这样写还不够好
// public class BinarySearchTree<E extends Comparable<? super E>>
// Comparable<? super E>表示能对E的父类进行比较
// 那能对E的父类进行比较,就一定能对E进行比较
public class BinarySearchTree<E extends Comparable<? super E>> {
//属性
private TreeNode root;
private int size;
private class TreeNode {
TreeNode left;
E value;
TreeNode right;
public TreeNode(E value) {
this.value = value;
}
public TreeNode(TreeNode left, E value, TreeNode right) {
this.left = left;
this.value = value;
this.right = right;
}
}
//构造方法
//刚开始创建一颗空树,size = 0, root = null就可以不用写构造方法
//方法
/**
* 在BST中插入一个元素
*
* @param key 待插入的元素
* @return 如果插入成功返回true,失败返回false
*/
/*
public boolean add(E key) {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
//处理空树情况
if (root == null) {
root = new TreeNode(key);
size++;
return true;
}
TreeNode p = null;
TreeNode x = root;
int cmp;
do {
cmp = key.compareTo(x.value);
if (cmp < 0) {
//往左走
p = x;
x = x.left;
} else if (cmp > 0) {
//往右走
p = x;
x = x.right;
} else return false;
} while (x != null);
//插入结点
TreeNode node = new TreeNode(key);
if (cmp > 0) p.right = node;
else p.left = node;
size++;
return true;
}
*/
//add方法的递归实现
public boolean add(E key) {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
int oldSize = size;
// 在root这棵树中插入key,并把新的根节点赋值给root
//不能直接调用add(E key)这个方法,这个方法不能把问题规模给减小
// 也就是说,我只知道要插入key,不知道在哪个位置插入key
root = add(root, key);
return oldSize < size;
}
private TreeNode add(TreeNode node, E key) {
//边界条件
//node == null 说明这就是要插入的位置
if (node == null) {
size++;
return new TreeNode(key);
}
int cmp = key.compareTo(node.value);
if (cmp < 0) {
//在左子树中插入key,并链接到node.left
node.left = add(node.left, key);
} else if (cmp > 0) {
//在右子树中插入key,并链接到node.right
node.right = add(node.right, key);
}
//如果触cmp == 0,就什么也不做
return node;
}
/**
* 删除BST与指定对象相等的元素
*
* @param key 执行对象
* @return 如果删除成功返回true,否则返回false
*/
/* public boolean remove(E key) {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
TreeNode p = null;
TreeNode x = root;
while (x != null) {
int cmp = key.compareTo(x.value);
if (cmp < 0) {
p = x;
x = x.left;
} else if (cmp > 0) {
p = x;
x = x.right;
} else break;
}
//x == null或者break
//BST中没有与指定对象相等的结点
if (x == null) return false;
//找到,break出来了,x就是我们要删除的结点
// x的度为2的情况
if (x.left != null && x.right != null) {
//找右子树的最小结点
TreeNode minP = x;
TreeNode minX = x.right;
while (minX.left != null) {
minP = minX;
minX = minX.left;
}
//用右子树的最小值结点替换x结点的值
x.value = minX.value;
//把情况退化成度为1或度为0的情况
p = minP;
x = minX;
}
//x结点就是要删除的结点,并且x结点的度为0或1
TreeNode child = x.left != null ? x.left : x.right;
//判断是否是根节点
if (p == null) p = child;
else if (p.left == x) p.left = child;
else p.right = child;
size--;
return true;
}*/
//remove方法的递归实现
public boolean remove(E key) {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
int oldSize = size;
// 在root这棵树中删除与key相等的元素,并把删除元素后的根节点赋值给root
root = remove(root, key);
return oldSize > size;
}
private TreeNode remove(TreeNode node, E key) {
//边界条件
if (node == null) return null;
int cmp = key.compareTo(node.value);
if (cmp < 0) node.left = remove(node.left, key);
else if (cmp > 0) node.right = remove(node.right, key);
else {
//cmp == 0表示我们删除的就是这个结点
if (node.left != null && node.right != null) {
//找右子树的最小结点
TreeNode minOfRight = node.right;
while (minOfRight.left != null) {
minOfRight = minOfRight.left;
}
node.value = minOfRight.value;
node.right = remove(node.right, minOfRight.value);
} else {
//度为0或者1的情况
TreeNode child = node.left != null ? node.left : node.right;
size--;
//返回孩子结点,就相当于删除了该结点
return child;
}
}
return node;
}
/**
* 判断BST中是否有和指定对象相等的元素
*
* @param key 指定对象
* @return 如果存在返回true,否则返回false
*/
public boolean contains(E key) {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
TreeNode x = root;
while (x != null) {
int cmp = key.compareTo(x.value);
if (cmp < 0) x = x.left;
else if (cmp > 0) x = x.right;
else return true;
}
//x == null,退出循环
return false;
}
/**
* 求BST中的最小值
*
* @return BST中的最小值
*/
public E min() {
if (this.isEmpty()) {
throw new NoSuchElementException("BST is Empty!");
}
TreeNode x = root;
while (x.left != null) x = x.left;
return x.value;
}
/**
* 求BST中的最大值
*
* @return BST中的最大值
*/
public E max() {
if (this.isEmpty()) {
throw new NoSuchElementException("BST is Empty");
}
return max(root).value;
}
private TreeNode max(TreeNode node) {
if (node.right != null) return max(node.right);
return node;
}
/**
* 清空BST中所有元素
*/
public void clear() {
root = null;
size = 0;
}
/**
* 判空
*
* @return
*/
public boolean isEmpty() {
return root == null;
}
/**
* 获取BST中元素的个数
*
* @return 元素的个数
*/
public int size() {
return size;
}
/**
* 获取树的高度,空树高度为0
*
* @return 数的高度
*/
public int height() {
return height(root);
}
private int height(TreeNode node) {
if (node == null) return 0;
int leftHeight = height(node.left);
int rightHeight = height(node.right);
return Math.max(leftHeight, rightHeight) + 1;
}
/**
* BST先序遍历
*
* @return 先序遍历列表
*/
public List<E> preOrder() {
List<E> list = new ArrayList<>();
//遍历BST,并把元素放入list中
preOrder(root, list);
return list;
}
private void preOrder(TreeNode node, List<E> list) {
//边界条件 node == null表示遍历到了叶子结点 直接返回即可
if (node == null) return;
//遍历根节点
list.add(node.value);
//遍历左子树
preOrder(node.left, list);
//遍历右子树
preOrder(node.right, list);
}
//用栈实现先序遍历
public List<E> preOrder2() {
List<E> list = new ArrayList<>();
if (isEmpty()) return list;
//将根节点入栈
Deque<TreeNode> stack = new LinkedList<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode x = stack.pop();
list.add(x.value);
if (x.right != null) stack.push(x.right);
if (x.left != null) stack.push(x.left);
}
return list;
}
/**
* BST中序遍历
*
* @return 中序遍历列表
*/
public List<E> inOrder() {
ArrayList<E> list = new ArrayList<>();
//遍历BST,并把元素放入list中
inOrder(root, list);
return list;
}
private void inOrder(TreeNode node, ArrayList<E> list) {
if (node == null) return;
//遍历左子树
inOrder(node.left, list);
//遍历根节点
list.add(node.value);
//遍历右子树
inOrder(node.right, list);
}
//用栈实现中序遍历
public List<E> inOrder2() {
List<E> list = new ArrayList<>();
if (this.isEmpty()) return list;
Deque<TreeNode> stack = new LinkedList<>();
TreeNode x = root;
while (x != null || stack.isEmpty()) {
//x结点不为空时
while (x != null) {
stack.push(x);
x = x.left;
}
//x == null,栈顶元素的左子树遍历完了
x = stack.pop();
list.add(x.value);
//往右走
x = x.right;
}
return list;
}
/**
* BST后序遍历
*
* @return 后序遍历列表
*/
public List<E> postOrder() {
ArrayList<E> list = new ArrayList<>();
postOrder(root, list);
return list;
}
private void postOrder(TreeNode node, ArrayList<E> list) {
//边界条件
if (node == null) return;
//遍历左子树
postOrder(node.left, list);
//遍历右子树
postOrder(node.right, list);
//遍历根节点
list.add(node.value);
}
//用栈实现后序遍历
public List<E> postOrder2() {
//用链表来存储后序遍历的元素
LinkedList<E> list = new LinkedList<>();
if (this.isEmpty()) return list;
//将根节点入栈
Deque<TreeNode> stack = new LinkedList<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode x = stack.pop();
//在链表表头添加元素
list.addFirst(x.value);
if (x.left != null) stack.push(x.left);
if (x.right != null) stack.push(x.right);
}
return list;
}
/**
* 层次遍历
*
* @return 层次遍历的列表
*/
public List<E> levelOrder() {
List<E> list = new ArrayList<>();
if (isEmpty()) return list;
//将根节点入队列
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
//判断队列是否为空
while (!queue.isEmpty()) {
//出队一个元素并添加到list中
TreeNode x = queue.poll();
list.add(x.value);
if (x.left != null) queue.offer(x.left);
if (x.right != null) queue.offer(x.right);
}
//队列为空,返回list
return list;
}
/**
* 层次遍历,但可以知道每个元素在第几层
*
* @return 层次遍历的列表
*/
//每一层用一个list表示,如:[[C],[A,D],[B,E]]
public List<List<E>> levelOrder1() {
List<List<E>> list = new ArrayList<>();
if (isEmpty()) return list;
//将根节点入队列
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
//判断队列是否为空
while (!queue.isEmpty()) {
//记录这一层元素的个数
int count = queue.size();
List<E> temp = new ArrayList<>();
for (int i = 0; i < count; i++) {
TreeNode x = queue.poll();
temp.add(x.value);
if (x.left != null) queue.offer(x.left);
if (x.right != null) queue.offer(x.right);
}
list.add(temp);
}
//队列为空,返回list
return list;
}
/**
* 根据先序和中序构建树
*
* @param preOrder 先序遍历列表
* @param inOrder 中序遍历列表
* @param <T> 元素类型
* @return 构建的树
*/
public static <T extends Comparable<? super T>> BinarySearchTree<T> buildTree(List<T> preOrder, List<T> inOrder) {
BinarySearchTree<T> tree = new BinarySearchTree<T>();
//BST有两个属性:size和root
tree.size = preOrder.size();
// 构建整棵树
tree.root = tree.build(preOrder, inOrder);
return tree;
}
private TreeNode build(List<E> preOrder, List<E> inOrder) {
// 边界条件
if (preOrder == null || preOrder.isEmpty()) return null;
// 构建根结点
E key = preOrder.get(0);
TreeNode node = new TreeNode(key);
int index = inOrder.indexOf(key); //[0, index), [index+1, size)
// 构建左子树
List<E> leftPreOrder = preOrder.subList(1, index + 1);
List<E> leftInOrder = inOrder.subList(0, index);
node.left = build(leftPreOrder, leftInOrder);
// 构建右子树
List<E> rightPreOrder = preOrder.subList(index + 1, preOrder.size());
List<E> rightInOrder = inOrder.subList(index + 1, inOrder.size());
node.right = build(rightPreOrder, rightInOrder);
return node;
}
/**
* 根据后序和中序构建树
*
* @param postOrder 后序遍历列表
* @param inOrder 中序遍历列表
* @param <T> 元素类型
* @return 构建的树
*/
public static <T extends Comparable<? super T>> BinarySearchTree<T> buildTree2(List<T> postOrder, List<T> inOrder) {
//首先创建一个BST类对象
BinarySearchTree<T> tree = new BinarySearchTree<>();
//给此对象的两个属性赋值
tree.size = postOrder.size();
tree.root = tree.build2(postOrder, inOrder);
return tree;
}
private TreeNode build2(List<E> postOrder, List<E> inOrder) {
//边界条件
if (postOrder == null || postOrder.isEmpty()) return null;
//构建根节点
E key = postOrder.get(postOrder.size() - 1);
TreeNode node = new TreeNode(key);
int index = inOrder.indexOf(node.value);
//构建左子树
List<E> LeftPostOrder = postOrder.subList(0, index);
List<E> LeftInOrder = inOrder.subList(0, index);
node.left = build2(LeftPostOrder, LeftInOrder);
//构建右子树
List<E> RightPostOrder = postOrder.subList(index, postOrder.size() - 1);
List<E> RightInOrder = inOrder.subList(index + 1, inOrder.size());
node.right = build2(RightPostOrder, RightInOrder);
return node;
}
public static void main(String[] args) {
BinarySearchTree<Character> tree = new BinarySearchTree<>();
tree.add('C');
tree.add('A');
tree.add('D');
tree.add('B');
tree.add('E');
// tree.add('E');
// System.out.println(tree.size());
// System.out.println(tree.preOrder2()); //[C, A, B, D, E]
// System.out.println(tree.inOrder()); // [A, B, C, D, E]
// System.out.println(tree.postOrder2());//[B, A, E, D, C]
// 删除不存在的结点
/* System.out.println(tree.remove('X'));
System.out.println(tree.size());*/
// 删除度为0的结点
/* System.out.println(tree.remove('B'));
System.out.println(tree.size());
System.out.println(tree.preOrder()); //[C, A, D, E]
System.out.println(tree.inOrder());*/
// 删除度为1的结点
/* System.out.println(tree.remove('A'));
System.out.println(tree.size());
System.out.println(tree.preOrder()); //[C, B, D, E]
System.out.println(tree.inOrder());*/
// 删除度为2的结点
/*System.out.println(tree.remove('C'));
System.out.println(tree.size());
System.out.println(tree.preOrder()); //[D, A, B, E]
System.out.println(tree.inOrder());*/
/*System.out.println(tree.inOrder());
System.out.println(tree.size());
System.out.println(tree.isEmpty());
tree.clear();
System.out.println(tree.inOrder());
System.out.println(tree.size());
System.out.println(tree.isEmpty());*/
// boolean contains(E key)
/*System.out.println(tree.contains('X'));
System.out.println(tree.contains('A'));*/
// E min(), E max()
/*System.out.println(tree.min()); //'A'
System.out.println(tree.max()); //'E'*/
// List<E> levelOrder()
// System.out.println(tree.levelOrder()); //[C, A, D, B, E]
// List<List<E>> levelOrder1()
// System.out.println(tree.levelOrder1());
// BinarySearTree buildTree(List preOrder, List inOrder)
// List<Character> preOrder = Arrays.asList('C', 'A', 'B', 'D', 'E');
// List<Character> postOrder = Arrays.asList('B', 'A', 'E', 'D', 'C');
// List<Character> inOrder = Arrays.asList('A', 'B', 'C', 'D', 'E');
// // BinarySearchTree<Character> tree = BinarySearchTree.buildTree(preOrder, inOrder);
// BinarySearchTree<Character> tree = BinarySearchTree.buildTree2(postOrder, inOrder);
// System.out.println(tree.size());
// System.out.println(tree.preOrder());// [C, A, B, D, E]
// System.out.println(tree.inOrder()); // [A, B, C, D, E]
// System.out.println(tree.postOrder()); //[B, A, E, D, C]
// int height()
System.out.println(tree.height());
tree.clear();
System.out.println(tree.height());
}
}
几个问题:
Q1: BST的性能如何?增加,删除和查找的时间复杂度为多少?
增加:O(h)
删除:O(h)
查找:O(h)
Q2: 普通BST不能保证树的平衡,特别是:
假设开始时树是平衡的,但是随着动态的添加和删除(当度为2的时候每次都是从右子树中删除)
导致树最终往左倾斜,性能越来越差。
最坏情况下,就变成单支树,时间复杂度变成O(n) (退化成链表)
Q3: 自平衡的二叉搜索树
avl树:每个结点左子树的高度和右子树的高度之差不超过1
红黑树:通过红黑规则保证树的高度是log(n)的级别。
AVL vs 红黑树
a. 红黑树的高度稍高于AVL树
b. 添加和删除元素,红黑树所做的调整比AVL树少。
从工程角度看,红黑树性能稍微优于AVL树。
···············································································································································································································
接下来讲一下Set和Map,红黑树稍后会带大家实现。
Set
一个不包含重复元素的 collection。更确切地讲, set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2, 并且最多包含一个 null 元素。正如其名称所暗示的,此接口模仿了数学上的 set 抽象。
注意事项:Set 集合并不一定都是无序的,有些 Set 集合是有序的。
HashSet是无序,并且可以存储null元素
TreeSet是有序的(按字母顺序排序),不可以存储null元素(会报空指针异常)
TreeSet底层是红黑树
set的方法都是继承自Collection,并没有自己定义的方法。