一、树形结构VS线性结构
动态数组 | 单向链表 | 双向链表 | 二叉搜索树 | |
---|---|---|---|---|
增加的时间复杂度 | O(N) | O(N) + O(1) | O(N) + O(1) | O(log N) |
删除的时间复杂度 | O(N) | O(N) + O(1) | O(N) + O(1) | O(log N) |
查询的时间复杂度 | O(1) | O(N) | O(N) | O(log N) |
二、树的基本概念梳理
结点的度:子树的个数。
树的度:所有结点度中的最大值。
结点的高度:当前结点到最远叶子结点路径上的结点总数。
结点的深度:根结点到当前结点唯一路径上的结点总数。
树的高度:所有结点中的最大高度。
树的深度:所有结点中的最大深度。
树的高度和深度相等。
N叉树:每个结点的最大度为N。
真二叉树:所有结点的度为2或者0。
满二叉树:每一层都排满结点,而且有第n层结点数为2^(n-1), n层的满二叉树结点总数为2^n-1, n层的满二叉树高度为log2(n+1)
。
完全二叉树:叶子节点只能分布在最后一层和最后第二层,最后层叶子结点一定先排满左边。
完全二叉树一些性质:
1、高度为h的完全二叉树结点最多是2 ^h-1,此时是满二叉树;结点最少是2 ^(h-1)。
2、N个结点的完全二叉树高度为floor(log2 N)+1
,可由性质2的不等式推导出来。
3、度为1的结点数只能是1或者0。
4、nm 表示度为m的结点,有n2 + 1 = n0
。
证明依据:结点总数 = 总边数(n0 x 0 + n1 x 1 + n2 x 2) + 1 = n0 + n1 + n2
二、二叉搜索树
1、BST简介
二叉搜索树也叫二叉排序树、二叉查找树。Binary Search Tree
满足下面三个条件:
① 任意一个结点的值都大于其左子树所有结点的值 ;
② 任意一个结点的值都小于其右子树所有结点的值 ;
③ 它的左右子树也是一棵二叉搜索树。
下图就是一棵简单的二叉搜索树:
2、BST的设计
定义一个抽象的基类,装一些二叉树通用的成员方法,二叉搜索树继承这个父类。
这两个都用泛型,二叉搜索树因为需要比较大小,考虑下面两种方案:
① public class BinarySearchTree<E extends Comparable>
肯定不可取,只有实现接口才能通过编译。
② 组合一个比较器Comparator
,完全解决顾虑,在构造函数中传入参数,当想要插入引用数据类型的时候,可以传入匿名类搞定。
3、左右旋转图解
三、二叉搜索树代码
1、秘籍
二叉树共有的方法:
前序遍历:先访问根结点,再前序遍历访问左子树,最后前序遍历访问右子树。
中序遍历:先中序遍历访问左子树,再访问根结点,最后中序遍历访问右子树。BST中序遍历一定是升序或者降序
后序遍历:先后序遍历访问左子树,再后序遍历访问右子树,最后访问根结点。
层次遍历:从上到下,从左到右,八个字言简意赅。计算二叉树高度、判断是否是完全二叉树
寻找A结点的前驱结点:中序遍历时的前一个结点
。如果A结点有左子树的话,前驱结点就是左子树上最右边的结点。如果A结点没有左子树的话,前驱结点就是其父结点中第一个是右子树的结点。
寻找A结点的后继结点:中序遍历时的后一个结点
。参照BST理解为结点值大于A结点的值,而且最靠近A的值。如果A结点有右子树的话,后继结点就是右子树上最左边的结点。如果A结点没有右子树的话,后继结点就是其父结点中第一个是左子树的结点。
二叉搜索树特有方法:
插入:比大小插入即可。
删除:删除某个结点按度分为三种情况。当度为0时直接删掉叶子结点;当度为1时找自己的儿子代替自己的位置;当度为2时,先找到该结点的前驱结点或者后继结点,将找到结点的数据覆盖掉要删除结点的数据,再将前驱结点或后继结点删除(此时这两个结点的度要么是1,要么是0)。
2、代码
BinaryTree代码:
public abstract class BinaryTree<E> {
protected Node<E> root;
protected int size = 0;
protected class Node<E> {
Node<E> left;
Node<E> right;
Node<E> parent;
E element;
public Node(Node<E> parent, E element) {
this.parent = parent;
this.element = element;
}
@Override
public String toString() {
StringBuilder str = new StringBuilder();
str.append("Node{");
// str.append(" parent=");
// if (parent != null)
// str.append(parent.element);
// else
// str.append("null");
str.append(", element=").append(element).append("}");
return str.toString();
}
}
public int size() {
return this.size;
}
public boolean isEmpty() {
return size == 0;
}
public void clear() {
size = 0;
root = null;
}
/**
* 获取某个结点的度
*/
protected int getDegree(Node<E> node) {
int s = 0;
if (node.left != null)
s++;
if (node.right != null)
s++;
return s;
}
/**
* 二叉树前序遍历
*/
public void preOrderTraversal() {
preOrder(root);
}
private void preOrderTraversal(Node<E> node) {
if (node == null)
return;
System.out.print(node.element + " "); //最外层递归root并没有变
preOrderTraversal(node.left);
preOrderTraversal(node.right);
}
/**
* 非递归二叉树前序遍历
*/
private void preOrder(Node<E> node) {
Stack<Node<E>> stack = new Stack<>();
while (true) {
if (node != null) {
System.out.print(node.element + " ");
if (node.right != null)
stack.push(node.right);
node = node.left;
} else {
if (!stack.isEmpty())
node = stack.pop();
else
break;
}
}
}
/**
* 二叉树中序遍历
*/
public void inOrderTraversal() {
inOrder(root);
}
private void inOrderTraversal(Node<E> node) {
if (node == null)
return;
inOrderTraversal(node.left);
System.out.print(node.element + " "); //最外层递归root并没有变
inOrderTraversal(node.right);
}
/**
* 非递归二叉树中序遍历
*/
private void inOrder(Node<E> node) {
Stack<Node<E>> stack = new Stack<>();
while (true) {
if (node != null) {
stack.push(node);
node = node.left;
} else {
if (!stack.isEmpty()) {
node = stack.pop();
System.out.print(node.element + " ");
} else
break;
node = node.right;
}
}
}
/**
* 二叉树后序遍历
*/
public void postOrderTraversal() {
postOrder(root);
}
private void postOrderTraversal(Node<E> node) {
if (node == null)
return;
postOrderTraversal(node.left);
postOrderTraversal(node.right);
System.out.print(node.element + " "); //最外层递归root并没有变
}
/**
* 非递归二叉树后序遍历
*/
private void postOrder(Node<E> node) {
Stack<Node<E>> stack = new Stack<>();
stack.push(node);
Node<E> prevNode = null;
while (!stack.isEmpty()) {
Node<E> top = stack.peek();
if ((top.left == null && top.right == null) || (prevNode != null && prevNode.parent == top)) {
prevNode = stack.pop();
System.out.print(prevNode.element + " ");
} else {
if (top.right != null)
stack.push(top.right);
if (top.left != null)
stack.push(top.left);
}
}
}
/**
* 二叉树层次遍历
*/
public void levelTraversal() {
Queue<Node<E>> queue = new LinkedList<>();
if (root == null)
return;
queue.add(root);
while (!queue.isEmpty()) {
Node<E> t = (Node<E>) queue.remove();
System.out.print(t.element + " "); //访问根结点
if (t.left != null)
queue.add(t.left); //左子树入队
if (t.right != null)
queue.add(t.right); //右子树入队
}
}
/**
* 获取某个结点的前驱结点
*/
public Node<E> getPrecursor(Node<E> node) {
Node<E> t = node.left;
if (t != null) { //有左子树就遍历左子树的右子树
while (t.right != null) {
t = t.right;
}
return t;
} else { //说明没有左子树就从父结点找
Node<E> parent = node;
while (parent != null) {
if (parent.parent == null)
return null;
if (parent.parent.right == parent)
return parent.parent;
parent = parent.parent;
}
return null;
}
}
/**
* 获取某个结点的后继结点
*/
public Node<E> getSuccessor(Node<E> node) {
Node<E> t = node.right;
if (t != null) { //有右子树就遍历右子树的左子树
while (t.left != null) {
t = t.left;
}
return t;
} else { //说明没有右子树就从父结点找
Node<E> parent = node;
while (parent != null) {
if (parent.parent == null)
return null;
if (parent.parent.left == parent)
return parent.parent;
parent = parent.parent;
}
return null;
}
}
/**
* 判断是否是完全二叉树
*/
public boolean isComplete() {
Queue<Node<E>> queue = new LinkedList<>();
queue.add(root);
boolean leaf = false;
while (!queue.isEmpty()) {
Node<E> node = queue.remove();
//总共情况: 1、左为空,右为空; 2、左为空,右不为空(false); 3、左不为空,右为空; 4、左右都不为空
//1 和 3 的情况出现后, 之后访问的结点都是叶子结点才是true
if (node.left != null) {
if (leaf)
return false;
queue.add(node.left);
} else if (node.right != null) { //说明左子树为空, 右子树不为空, 一定不是完全二叉树
return false;
}
if (node.right != null) { //能走到这里说明不会是情况2
if (leaf)
return false;
queue.add(node.right);
} else //1 和 3情况
leaf = true;
}
return true;
}
/**
* 获取二叉树的高度
*/
public int getHeight() {
return height(root);
}
private int height(Node<E> node) {
Queue<Node<E>> queue = new LinkedList<>();
queue.add(root);
int size = 1;
int height = 0;
while (!queue.isEmpty()) {
Node<E> t = queue.remove();
size--;
if (t.left != null)
queue.add(t.left);
if (t.right != null)
queue.add(t.right);
if (size == 0) {
height++;
size = queue.size();
}
}
return height;
}
}
BinarySearchTree代码:
public class BinarySearchTree<E> extends BinaryTree<E> {
private Comparator<E> comparator;
public BinarySearchTree(Comparator<E> comparator) {
this.comparator = comparator;
}
public BinarySearchTree() {
this(null); //调用有参构造
}
private int compare(E e1, E e2) {
if (comparator != null)
return comparator.compare(e1, e2);
else
return ((Comparable) e1).compareTo(e2);
}
public boolean contains(E element) {
return getNode(element) != null;
}
public Node<E> getNode(E element) {
Node<E> node = root;
while (node != null) {
int cmp = compare(element, node.element);
if (cmp > 0) {
node = node.right;
} else if (cmp < 0) {
node = node.left;
} else {
return node;
}
}
return null;
}
/**
* BST插入一个结点
*
* @param element
*/
public void add(E element) {
Node<E> node = root;
if (root == null) { //插入第一个结点
root = new Node(null, element);
size++;
return;
}
int cmp = 0;
Node<E> parent = null;
while (node != null) {
parent = node;
cmp = compare(element, node.element);
if (cmp > 0) { //待插入的元素比根结点元素大
node = node.right;
} else if (cmp < 0) { //待插入的元素比根结点元素小
node = node.left;
} else {
return;
}
}
if (cmp > 0)
parent.right = new Node<E>(parent, element);
else
parent.left = new Node<E>(parent, element);
size++;
}
/**
* BST删除节点要分情况, 度为0、1、2
*
* @param element
*/
public void remove(E element) {
Node<E> node = getNode(element);
if (node == null)
return;
if (getDegree(node) == 2) {
Node<E> t = getSuccessor(node);
node.element = t.element;
node = t; //复用代码
}
if (getDegree(node) == 0) { //度为0, 直接删除即可
if (root == node) { //只有一个根结点
root = null;
return;
}
if (node == node.parent.left)
node.parent.left = null;
else
node.parent.right = null;
} else if (getDegree(node) == 1) { //度为1, 找自己的左儿子或右儿子替代
if (node.left != null) { //找左儿子代替
if (root == node) { //只有一个根结点
root = node.left;
return;
}
if (node.parent.left == node) {
node.parent.left = node.left;
node.left.parent = node.parent; //改变父结点
} else {
node.parent.right = node.left;
node.left.parent = node.parent; //改变父结点
}
} else { //找右儿子代替
if (root == node) { //只有一个根结点
root = node.right;
return;
}
if (node.parent.left == node) {
node.parent.left = node.right;
node.right.parent = node.parent; //改变父结点
} else {
node.parent.right = node.right;
node.right.parent = node.parent; //改变父结点
}
}
}
}
}