二叉树:
- 二叉树结构
1.二分搜索树的应用:(二分搜索类似二分查找,需要已排序)
2.二分树的特性:
虽然二分树通常情况下不包含重复元素,但是在进行add和remove时,只要改变判断条件,就可以包括重复元素。要注意和 堆进行区分,堆实际上借鉴了二分树的思想。不同的是,二分树的排序特性为从左叶子节点向右递增。而堆是从 root 向下递减(分为最大堆和最小堆)
3.具体实现
// 父节点的值,大于左子节点,大于右子节点
public class BTS <E extends Comparable<E>>{
// 使用链表实现
private class Node{
public E value;
public Node left;
public Node right;
public Node(E e){
value = e;
left = null;
right = null;
}
}
private Node root;
private int size;
public BTS(){
root = null;
size = 0;
}
// 加入元素,放到适合的位置
public Node add(Node node, E e){
// 如果为空二叉树,则为头节点
if(Node == null){
size++;
return new Node(e);
}
// 否则进行遍历
if(e.compareTo(node.e) < 0){
node.left = add(node.left, e);
}else{
node.right = add(node.right, e);
}
return node;
}
// 使用非递归方法
private Node add2(Node node, E e) {
while (node != null) {
if (e.compareTo(node.e) < 0) {
node = node.left;
} else if (e.compareTo(node.e) > 0) {
node = node.right;
}
}
node = new Node(e);
return node;
}
}
(1)假设根节点为 null ,满足
(2) 看递归过程 :
// 在 node == null 时
看上去是 node 被赋值为 new Node(e), 实际上 return 时 ,node会消失,不存在于 node.left中。
因为 node = new Node(e) 仅在if( node == null) 时生效, node.left 并不会改变
就好比 你传入 (2,3)
然后 3 递减, 当 小于1时,2 == 1,但是局部赋值不能改变传递进来的参数.
因此只有返回值才能保证变化。
1.删除节点:
假如为叶子节点:直接删除
假如有左子节点或者右子节点,直接上移
假如左子节点和右子节点同时存在,看以下情况:
前缀:寻找当前节点的左子树中最大的那个节点
后缀:寻找当前节点的右子树中最小的那个节点
// 删除某节点下,value = e 的节点,左子树中最大的那个提上来作为新的父节点
private Node remove(Node node, E e){
// 遍历到叶子节点后
if(node == null) return null;
// 遍历直到 node.e == value
// 假如
if(e.compareTo(node.e) < 0){
node.left = remove(node.left, e);
return node;
}else if(e.compareTo(node.e) > 0) {
node.right = remove(node.right, e);
return node;
}else{
// 假如当前节点左子树为空,即为叶子节点
if (node.left == null) {
Node accessor = node.right;
node.right = null;
size--;
return accessor;
// 假如当前右子树为空,为叶子节点
} else if (node.right == null) {
Node accessor = node.left;
node.left = null;
size--;
return accessor;
}
// 假如当前节点节点为满
Node accessor = Maximum(node.right);
accessor.left = node.left;
accessor.right = removeMin(node.right);
node.left = null;
node.right = null;
return accessor;
}
}
// 删除某节点后,右子树中最小的那个提上来作为新的父节点
private Node remove2(Node node, E e) {
if (node == null) {
return null;
}
if (e.compareTo(node.e) < 0) {
node.left = remove2(node.left, e);
return node;
} else if (e.compareTo(node.e) > 0) {
node.right = remove2(node.right, e);
return node;
} else {
if (node.left == null) {
Node accessor = node.right;
node.right = null;
size--;
return accessor;
} else if (node.right == null) {
Node accessor = node.left;
node.left = null;
size--;
return accessor;
}
Node accessor = Maximum(node.left);
accessor.right = node.right;
accessor.left = removeMax(node.left);
node.left = null;
node.right = null;
return accessor;
}
}
- 获取Node 节点下最小值与最大值
```java
// 获取 Node 节点下最大值
public Node Maximum(Node node){
if(node.right == null)
return Maximum(node.right); }return node;
// 获取 Node 节点下最大值 public Node Minimum(Node node){ if(node.left == null) return node; return Minimum(node.left); }
- 删除 Node 节点下最小值与最大值
```java
public Node removeMax(Node node){
// 由于左子节点小于node
if(node.right == null){
Node nodeleft = node.left;
node.left = null;
size--;
return nodeleft;
}
node.right = removeMax(node.right);
return node;
}
public Node removeMin(Node node){
// 由于左子节点小于node
if(node.left == null){
Node nodeleft = node.right;
node.right = null;
size--;
return noderight;
}
node.right = removeMax(node.right);
return node;
}
2.遍历方式:
前序遍历(preOrder)
// 前序遍历 public void preOrder(Node node){ // 当 node == null,则直接返回 if(node == null){ return; } System.out.println(node.e); preOrder(node.left); preOrder(node.right); }
中序遍历(inOrder)
// 中序遍历 public voud inOrder(Node node){ if(node == null){ return; } inOrder(node.left); System.out.println(node.e); inOrder(node.right); }
后序遍历 (postOrder)
// 后序遍历 public void postOrder(Node node){ if(node == null){ return; } postOrder(node.left); postOrder(node.right); System.out.println(node.e); }
层序遍历 (levelOrder)
// 层序遍历 public void levelOrder(){ Queue<Integer> queue = new Queue<>(); // 使用队列,每次都把 Node 加入,然后进行遍历 queue.add(root); while(!queue.isEnpty()){ Node cur = queue.remove(); System.out.println(cur); if(cur.left != null){ queue.add(cur.left); }else if(cur.right != null){ queue.add(cur.right); } } }