一、树的概念回顾
1、树具有以下特点:
1、每个结点有零个或多个子结点;
2、没有父结点的结点为根结点;
3、每一个非根结点只有一个父结点;
4、每个结点及其后代结点整体上可以看做是一棵树,称为当前结点的父结点的一个子树;
2、树的相关术语
结点的度:
一个结点含有的子树的个数称为该结点的度;
叶子结点:
度为0的结点称为叶结点,也可以叫做终端结点
分支结点:
度不为0的结点称为分支结点,也可以叫做非终端结点
结点的层次:
从根结点开始,根结点的层次为1,根的直接后继层次为2,以此类推
树的度:
树中所有结点的度的最大值
树的高度(深度):
树中结点的最大层次
森林:
m(m>=0)个互不相交的树的集合,将一颗非空树的根结点删去,树就变成一个森林;给森林增加一个统一的根结点,森林就变成一棵树
孩子结点:
一个结点的直接后继结点称为该结点的孩子结点
双亲结点(父结点):
一个结点的直接前驱称为该结点的双亲结点
兄弟结点:
同一双亲结点的孩子结点间互称兄弟结点
二、二叉树回顾
1、基本概念:
二叉树: 就是度不超过 2的树(每个结点最多有两个子结点)
满二叉树: 每一个层的结点树都达到最大值,则这个二叉树就是满二叉树。
完全二叉树: 叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树
上图的树都是二叉树
2、二叉搜索树Java实现
package com.xiaojie.tree;
import org.w3c.dom.Node;
import java.util.LinkedList;
import java.util.Queue;
/**
* @author Mrli
* @date 2020/9/12 15:44
*/
public class BinaryTree<Key extends Comparable<Key>, Value> {
/**
* 二叉搜索树结点
* @param <Key>
* @param <Value>
*/
private class Node<Key,Value>{
//存储键
public Key key;
//存储值
private Value value;
//记录左子结点
public Node left;
//记录右子结点
public Node right;
public Node(Key key, Value value, Node left, Node right) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
}
/**
* 记录根节点
*/
private Node root;
/**
* 记录树中元素个数
*/
private int N;
/**
* 获取树中元素的个数
* @return
*/
public int size() {
return N;
}
/**
* 向树中添加元素key-value
* @param key
* @param value
*/
public void put(Key key,Value value) {
root = put(root, key, value);
}
/**
* 给指定树x上,添加键一个键值对,并返回添
* 加后的新树
* @param x
* @param key
* @param val
* @return
*/
private Node put(Node x, Key key, Value val) {
if (x == null) {
N++;
return new Node(key,val,null,null);
}
int cmp = key.compareTo((Key) x.key);
if (cmp > 0) {
//新结点的key大于当前结点的key,继续找当前结点的右子结点
x.right = put(x.right, key, val);
} else if (cmp < 0) {
//新结点的key小于当前结点的key,继续找当前结点的左子结点
x.left = put(x.left, key, val);
} else {
//新结点的key等于当前结点的key,把当前结点的value进行替换
x.value = val;
}
return x;
}
/**
* 根据key,从树中找出对应的值
* @param key
* @return
*/
public Value get(Key key) {
return get(root,key);
}
/**
* 从指定的树x中,找出key对应的值
* @param x
* @param key
* @return
*/
private Value get(Node x, Key key) {
if (x == null) {
return null;
}
int cmp = key.compareTo((Key) x.key);
if (cmp > 0) {
//新结点的key大于当前结点的key,继续找当前结点的右子结点
return (Value) get(x.right, key);
} else if (cmp < 0) {
//新结点的key小于当前结点的key,继续找当前结点的左子结点
return (Value) get(x.left, key);
} else {
//新结点的key等于当前结点的key,把当前结点的value返回
return (Value) x.value;
}
}
/**
* 根据key,删除树中对应的键值对
* @param key
*/
public void delete(Key key) {
delete(root,key);
}
/**
* 删除指定树x上的键为key的键值对,并返回删除后的新树
* @param x
* @param key
* @return
*/
private Node delete(Node x, Key key) {
if (x == null) {
return null;
}
int cmp = key.compareTo((Key) x.key);
if (cmp > 0) {
//新结点的key大于当前结点的key,继续找当前结点的右子结点
x.right = delete(x.right, key);
} else if (cmp < 0) {
//新结点的key小于当前结点的key,继续找当前结点的左子结点
x.left = delete(x.left, key);
} else {
//删除结点的key等于当前结点的key,把当前结点的删除
//1.如果当前结点的右子树不存在,则直接返回当前结点的左子结点
if(x.right == null) {
//结点个数-1
N--;
return x.left;
}
//2.如果当前结点的左子树不存在,则直接返回当前结点的右子结点
if(x.left == null) {
//结点个数-1
N--;
return x.right;
}
//3.当前结点的左右子树都存在
//3.1找到右子树中最小的结点
Node minNode = x.right;
while (minNode.left != null) {
minNode = minNode.left;
}
//3.2删除右子树中最小的结点
Node del = x.right;
while(del.left != null) {
if(del.left.left != null) {
del = del.left;
} else {
del.left = null;
}
}
//3.3让被删除结点的左子树称为最小结点minNode的左子树
// 让被删除结点的右子树称为最小结点minNode的右子树
minNode.left = x.left;
minNode.right = x.right;
//3.4让被删除结点的父节点指向最小结点minNode
x = minNode;
//3.5 结点个数N--
N -= 1;
}
return x;
}
/**
* 找出树中最小的键
* @return
*/
public Key min() {
return (Key) min(root).key;
}
/**
* 找出指定树x中,最小键所在的结点
* @param x
* @return
*/
private Node min(Node x){
if(x.left != null) {
return min(x.left);
} else {
return x;
}
}
/**
* 找出树中最大的键
* @return
*/
public Key max() {
return (Key) min(root).key;
}
/**
* 找出指定树x中,最大键所在的结点
* @param x
* @return
*/
private Node max(Node x){
if(x.right != null) {
return min(x.right);
} else {
return x;
}
}
/**
* 使用前序遍历,获取整个树中的所有键
* @return
*/
public Queue<Key> preErgodic(){
Queue<Key> keys = new LinkedList<>();
preErgodic(root,keys);
return keys;
}
/**
* 使用前序遍历,把指定树x中的所有键放入到keys队列中
* @param x
* @param keys
*/
private void preErgodic(Node x,Queue<Key> keys) {
if(x == null) {
return;
}
keys.add((Key) x.key);
preErgodic(x.left, keys);
preErgodic(x.right, keys);
}
/**
* 使用中序遍历,获取整个树中的所有键
* @return
*/
public Queue<Key> midErgodic(){
Queue<Key> keys = new LinkedList<>();
midErgodic(root,keys);
return keys;
}
/**
* 使用中序遍历,把指定树x中的所有键放入到keys队列中
* @param x
* @param keys
*/
private void midErgodic(Node x,Queue<Key> keys) {
if(x == null) {
return;
}
midErgodic(x.left, keys);
keys.add((Key) x.key);
midErgodic(x.right, keys);
}
/**
* 使用后序遍历,获取整个树中的所有键
* @return
*/
public Queue<Key> afterErgodic(){
Queue<Key> keys = new LinkedList<>();
afterErgodic(root,keys);
return keys;
}
/**
* 使用后序遍历,把指定树x中的所有键放入到keys队列中
* @param x
* @param keys
*/
private void afterErgodic(Node x,Queue<Key> keys) {
if(x == null) {
return;
}
afterErgodic(x.left, keys);
afterErgodic(x.right, keys);
keys.add((Key) x.key);
}
/**
* 使用层序遍历得到树中所有的键
* @return
*/
public Queue<Key> layerErgodic(){
Queue<Key> keys = new LinkedList<>();
Queue<Node> nodes = new LinkedList<>();
nodes.add(root);
while(!nodes.isEmpty()) {
Node node = nodes.poll();
keys.add((Key) node.key);
if(node.left != null) {
nodes.add(node.left);
}
if(node.right != null) {
nodes.add(node.right);
}
}
return keys;
}
/**
* 计算整个树的最大深度
* @return
*/
public int maxDepth() {
return maxDepth(root);
}
/**
* 计算指定树x的最大深度
* @param x
* @return
*/
private int maxDepth(Node x) {
if(root == null) {
return 0;
}
int maxL = 0;
int maxR = 0;
//1.计算左子树的最大深度;
if(x.left != null) {
maxL = maxDepth(x.left);
}
//2.计算右子树的最大深度;
if(x.right != null) {
maxR = maxDepth(x.right);
}
return Math.max(maxL,maxR)+1;
}
}
二叉搜索树测试类:
package com.xiaojie.test;
import com.xiaojie.tree.BinaryTree;
import java.util.Queue;
/**
* @author Mrli
* @date 2020/9/12 16:20
*/
public class BinaryTreeTest {
public static void main(String[] args) {
//测试二叉树构建
//insertTest();
//测试二叉树前序遍历
//prePrint();
//测试二叉树中序遍历
//midPrint();
//测试二叉树后序遍历
//afterPrint();
//测试二叉树层序遍历
//layerPrint();
//测试获取树的深度
deepTest();
}
/**
* 获取二叉树的深度
*/
public static void deepTest() {
BinaryTree<String, String> bt = build();
System.out.println(bt.maxDepth());
}
/**
* 二叉树层次遍历
*/
public static void layerPrint() {
BinaryTree<String, String> bt = build();
Queue<String> queue = bt.layerErgodic();
for (String key : queue) {
System.out.println(key+"="+bt.get(key));
}
}
/**
* 二叉树后序遍历
*/
public static void afterPrint() {
BinaryTree<String, String> bt = build();
Queue<String> queue = bt.afterErgodic();
for (String key : queue) {
System.out.println(key+"="+bt.get(key));
}
}
/**
* 二叉树中序遍历
*/
public static void midPrint() {
BinaryTree<String, String> bt = build();
Queue<String> queue = bt.midErgodic();
for (String key : queue) {
System.out.println(key+"="+bt.get(key));
}
}
/**
* 二叉树前序遍历
*/
public static void prePrint() {
BinaryTree<String, String> bt = build();
Queue<String> queue = bt.preErgodic();
for (String key : queue) {
System.out.println(key+"="+bt.get(key));
}
}
/**
* 构建二叉树:插入、删除
* @return
*/
public static void insertTest() {
BinaryTree<String, String> bt = build();
System.out.println(bt.size());
bt.put("K","12");
System.out.println(bt.get("G"));
System.out.println(bt.size());
bt.delete("D");
System.out.println(bt.size());
System.out.println(bt.get("D"));
}
/**
* 二叉树构建
*/
public static BinaryTree<String, String> build() {
BinaryTree<String, String> bt = new BinaryTree<>();
bt.put("E", "5");
bt.put("B", "2");
bt.put("G", "7");
bt.put("A", "1");
bt.put("D", "4");
bt.put("F", "6");
bt.put("H", "8");
bt.put("C", "3");
return bt;
}
}
记录学习,记录生活!