基本定理:树中节点数等于边的数目加1,也等于所有节点的度数加1,,很显然嘛,因为边的个数就等于节点的度数
森林为m棵不想交的树,删除一个树的根,就能得到一个森林,相反,加上那个一个节点作为树根,森林就会变成树
树的核心在代码方面其实就是节点而已,任何一种结构不能只看他的逻辑结构,要看他的存储时的物理结构,而存储结构起始就只有两种,一种是顺序结构,另一种是连式结构,,以前在堆,二叉堆那里实现的树就是一种特殊的,用数组存储的树,但在实际中,并不多用数组来存储树,而是用链式来表示
import java.util.List;
class TreeNode<E>{
public E key; //data
public TreeNode<E> parent;
public List<TreeNode<E>> children;
public TreeNode(E key, TreeNode<E> parent) {
this.key = key;
this.parent = parent;
}
@Override
public String toString() {
return "TreeNode{" +
"key=" + key +
'}';
}
}
然后是树的基本操作,,我把他定义为一个接口类
import java.util.List;
public interface ITree<E> {
int getSize();
TreeNode<E> getRoot();
TreeNode<E> getParent(TreeNode<E> x);
TreeNode<E> getFirstChild(TreeNode<E> x);
TreeNode<E> getNextSibling(TreeNode<E> x);
int getHeight(TreeNode<E> x);
void insertChild(TreeNode<E> x, TreeNode<E> child);
void deleteChild(TreeNode<E> x, int i);
List<TreeNode<E>> preOrder(TreeNode<E> x);//xian
List<TreeNode<E>> postOrder(TreeNode<E> x);//hou
List<List<TreeNode<E>>> levelOrder(TreeNode<E> x);//ceng ci bian li
List<List<TreeNode<E>>> levelOrder();
}
这里只写的是一个接口类,具体的方法并没与去实现,然后下来我去实现一下具体的一个树,要只有一些些方法具有相关联的关系,所以记得改一个东西要去看看还有什么变化了吗,
另外,树中体现了深搜(树的高度)和宽搜(层次遍历) (层次遍历是一个很经典的问题哦)
深搜用递归,宽搜用队列 ,弹一个,(从前面弹出一个)加n个,,加n个意思是把他的n个孩子加进来(加到后面去),所以要用队列这样的数据结构,,,这样直接按层遍历也比较简单,但是还有一个小需求就是不要直接输出一个大列表,而是一个大列表里一行是一个小列表,这样也很简单啊,只要弹出的是上一层的最后一个节点,那就新开一个队列,
package org.lanqiao.algo.elementary._11_tree;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import static java.lang.Math.max;
public class MyTree<E> implements ITree<E> {
private int size = 0;
private TreeNode root;
public MyTree(TreeNode root) {
this.root = root;
size++;
}
@Override
public int getSize() {
return size;
}
@Override
public TreeNode<E> getRoot() {
return root;
}
@Override
public TreeNode<E> getParent(TreeNode<E> x) {
return x.parent;
}
@Override
public TreeNode<E> getFirstChild(TreeNode<E> x) {
return x.children.get(0);
}
@Override
public TreeNode<E> getNextSibling(TreeNode<E> x) {
List<TreeNode<E>> children = x.parent.children;
int i = children.indexOf(x);
// try {
// return children.get(i + 1);
// } catch (Exception e) {
// return null;
// }
if (i == children.size() - 1) {
return null;
} else {
return children.get(i + 1);
}
}
public int getHeight() {
return getHeight(root);
}
@Override
public int getHeight(TreeNode<E> x) {
if (x.children == null) {
return 0;
} else {
int h = 0;
for (int i = 0; i < x.children.size(); i++) {
h = max(h, getHeight(x.children.get(i)));
}
return h + 1;
}
}
@Override
public void insertChild(TreeNode<E> p, TreeNode<E> child) {
if (p.children == null) {
p.children = new ArrayList<>();
}
p.children.add(child);
child.parent = p;
size++;
}
@Override
public void deleteChild(TreeNode<E> p, int i) {
p.children.remove(i);
size--;
}
@Override
public List<TreeNode<E>> preOrder(TreeNode<E> x) {
return null;
}
@Override
public List<TreeNode<E>> postOrder(TreeNode<E> x) {
return null;
}
@Override
public List<List<TreeNode<E>>> levelOrder(TreeNode<E> x) { //安层打印,加一弹二
List<List<TreeNode<E>>> res = new ArrayList<>();//list的list
Queue<TreeNode<E>> q = new LinkedList<>();
q.add(x);//初始化
TreeNode<E> last = x;//标记上一行的最末节点
TreeNode<E> nLast = null;//标记最新加入队列的节点
List<TreeNode<E>> l = new ArrayList<>();//第一行的list
res.add(l);
while (!q.isEmpty()) {
TreeNode<E> peek = q.peek();
//把即将弹出的节点的子节点加入队列
if (peek.children != null) {
for (TreeNode<E> n : peek.children) {
q.add(n);
nLast = n;
}
}
l.add(q.poll());//弹出,加入到当前层列表
if (peek == last && !q.isEmpty()) {//如果现在弹出的节点是之前标记的最后节点,就要换列表
l = new ArrayList<>();
res.add(l);
last = nLast;
}
}
return res;
}
@Override
public List<List<TreeNode<E>>> levelOrder() {
return levelOrder(root);
}
}
二叉树
度为0的节点数的个数 = 度数为2的节点个数+1
完全二叉树:是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。
满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点二叉树。
有n个节点的完全二叉树的高度为logn
含有n>=1个节点的二叉树高度至多为n-1(直接退化成一根线,又叫斜二叉树),,高度至少为logn
对有n个节点的完全二叉树进行编号,则对于任意节点i(1=<i,,,意思死从1开始编号)
(1) i>1,他的双亲节点是i/2
(2)2×i>n,则i无左孩子,否则他左孩子是2×i;
(3)2×i+1>n,则i无右孩子,否则右孩子是2×i+1
那么如何写二叉树的代码呢?其实只需要把原来的树的List<TreeNode> children 改成2就可以了
二叉查找树(binary search tree,BST)
其实意思就是小的往左边走,大的往右边走,然后他的子树都是二叉树,如果按中序遍历一颗BST的话,就可以得到一个按照关键字递增的有序序列
public interface IBinarySearchTree<K,V> {
/**
* 新增节点
* @param k 关键字
* @param v 值
*/
BSTNode<K, V> insert(K k, V v);
/**
* 中序遍历
*
* @param con
* 处理中序遍历的每个元素的函数
*/
void inorder(Consumer<K> con);
/**
* 查找元素
* @param key
* @return
*/
V lookupValue(K key);
/**
* 获取最小关键字
* @return
*/
K min();
/**
* 获取最大关键字
* @return
*/
K max();
/**
* 移除关键字对应的节点
* @param key
*/
void remove(K key);
/**
* x的后继——比x大的第一个元素
* 1、是其右子树的最小值
* 2、没有右子树,则向上追溯,直到某个祖先节点是左孩子,返回这个祖先节点的父节点,它就是x的后继
* @param x
* @return
*/
K successor(K x);
/**
* 前驱
* @param x 关键字
* @return
*/
K predecessor(K x);
boolean isBalance();
/**
* 返回节点数
* @return
*/
int getSize();
/**
* 高度
* @return
*/
int getHeight();
List<List<BSTNode<K, V>>> levelOrder();
}