这里写自定义目录标题
- P79 79_树_树的定义
- P80 80_树_树的相关术语
- # P81 81_树_二叉树定义
- P82 82_树_二叉查找树创建_API设计
- P83 83_树_二叉查找树创建_插入方法
- P84 84_树_二叉查找树创建_获取方法
- P85 85_树_二叉查找树创建_删除方法
- P86 86_树_二叉查找树创建_测试
- P88 87_树_二叉查找树创建_查找最小键
- P89 88_树_二叉查找树创建_查找最大键
- P90 89_树_二叉树_遍历概述
- P91 90_树_二叉树_前序遍历
- P92 91_树_二叉树_中序遍历
- P93 92_树_二叉树_后序遍历
- P94 93_树_二叉树_层序遍历
- P95 94_树_二叉树_最大深度问题
- P96 95_树_二叉树_折纸问题
P79 79_树_树的定义
P80 80_树_树的相关术语
# P81 81_树_二叉树定义
每一层的结点个数:2^(k-1)
P82 82_树_二叉查找树创建_API设计
为什么要用泛型?
因为可以存储多个类型的数据。
P83 83_树_二叉查找树创建_插入方法
P84 84_树_二叉查找树创建_获取方法
P85 85_树_二叉查找树创建_删除方法
P86 86_树_二叉查找树创建_测试
删掉后,要找一个结点来替换。12比左子树大,12比右子树小!
把12删掉。
让原来的左子树成为12的左子树,
让原来的右子树成为12的右子树,
//得找到右子树中最小的结点
if (x.right==null){
return x.left;
}
//假如删除14,14的右子树为空,直接把12替换14就行了,
//这段代码就是这个意思。
if (x.left==null){
return x.right;
}
//假如说要删除25,25左子树为空。
//直接让右子树替换就行。
14结点的下一个结点(12),再下一个结点为空,把(12)删除
//删除右子树中最小的结点
Node n = x.right;
while(n.left!=null){
if (n.left.left==null){
n.left=null;
//14结点的下一个结点(12),再下一个结点为空,把(12)删除
}else{
//变换n结点即可,往下走
n = n.left;
}
}
package tree;
public class BinaryTree<Key extends Comparable<Key>, Value> {
//记录根结点
private Node root;
//记录树中元素的个数
private int N;
private class Node {
//存储键
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;
}
}
//获取树中元素的个数
public int size() {
return N;
}
//向树中添加元素key-value
public void put(Key key, Value value) {
root = put(root, key, value);
}
//向指定的树x中添加key-value,并返回添加元素后新的树
private Node put(Node x, Key key, Value value) {
//如果x子树为空,
if (x==null){
N++;
return new Node(key,value, null,null);
}
//如果x子树不为空
//比较x结点的键和key的大小:
int cmp = key.compareTo(x.key);
if (cmp>0){
//如果key大于x结点的键,则继续找x结点的右子树
x.right = put(x.right,key,value);
}else if(cmp<0){
//如果key小于x结点的键,则继续找x结点的左子树
x.left = put(x.left,key,value);
}else{
//如果key等于x结点的键,则替换x结点的值为value即可
x.value = value;
}
return x;
}
//查询树中指定key对应的value
public Value get(Key key) {
return get(root,key);
}
//从指定的树x中,查找key对应的值
public Value get(Node x, Key key) {
//x树为null
if (x==null){
return null;
}
//x树不为null
//比较key和x结点的键的大小
int cmp = key.compareTo(x.key);
if (cmp>0){
//如果key大于x结点的键,则继续找x结点的右子树
return get(x.right,key);
}else if(cmp<0){
//如果key小于x结点的键,则继续找x结点的左子树
return get(x.left,key);
}else{
//如果key等于x结点的键,就找到了键为key的结点,只需要返回x结点的值即可
return x.value;
}
}
//删除树中key对应的value
public void delete(Key key) {
delete(root, key);
}
//删除指定树x中的key对应的value,并返回删除后的新树
public Node delete(Node x, Key key) {
//x树为null
if (x==null){
return null;
}
//x树不为null
int cmp = key.compareTo(x.key);
if (cmp>0){
//如果key大于x结点的键,则继续找x结点的右子树
x.right = delete(x.right,key);
//delete删除后返回的树,作为x的右子树
}else if(cmp<0){
//如果key小于x结点的键,则继续找x结点的左子树
x.left = delete(x.left,key);
//delete删除后返回的树,作为x的左子树
}else{
//如果key等于x结点的键,完成真正的删除结点动作,要删除的结点就是x;
//让元素个数-1
N--;
//得找到右子树中最小的结点
if (x.right==null){
return x.left;
}
//假如删除14,14的右子树为空,直接把12替换14就行了,
//这段代码就是这个意思。
if (x.left==null){
return x.right;
}
//假如说要删除25,25左子树为空。
//直接让右子树替换就行。
//左右子树都不为空,找到右子树,后面一直找左边就行了
Node minNode = x.right;
while(minNode.left!=null){
minNode = minNode.left;
}
//通过while循环,找到最小的那个节点
//删除右子树中最小的结点
Node n = x.right;
while(n.left!=null){
if (n.left.left==null){
n.left=null;
}else{
//变换n结点即可
n = n.left;
}
}
//让x结点的左子树成为minNode的左子树
minNode.left = x.left;
//让x结点的右子树成为minNode的右子树
minNode.right = x.right;
//让x结点的父结点指向minNode
x = minNode;
//为什么可以这样写?
//因为上面是递归!,返回值就已经赋值为某树的左节点或者右节点。
// x.left = delete(x.left,key);
//x.right = delete(x.right,key);
}
return x;
}
}
package test;
import tree.BinaryTree;
public class BinaryTreeTest {
public static void main(String[] args) {
//创建二叉查找树对象
BinaryTree<Integer, String> tree = new BinaryTree<>();
//测试插入
tree.put(1,"张三");
tree.put(2,"李四");
tree.put(3,"王五");
System.out.println("插入完毕后元素的个数:"+tree.size());
//测试获取
System.out.println("键2对应的元素是:"+tree.get(2));
//测试删除
tree.delete(3);
System.out.println("删除后的元素个数:"+tree.size());
System.out.println("删除后键3对应的元素:"+tree.get(3));
}
}
P88 87_树_二叉查找树创建_查找最小键
P89 88_树_二叉查找树创建_查找最大键
//查找整个树中最小的键
public Key min(){
return min(root).key;
}
//在指定树x中找出最小键所在的结点
private Node min(Node x){
//需要判断x还有没有左子结点,如果有,则继续向左找,如果没有,则x就是最小键所在的结点
if (x.left!=null){
return min(x.left);
}else{
return x;
}
}
//在整个树中找到最大的键
public Key max(){
return max(root).key;
}
//在指定的树x中,找到最大的键所在的结点
public Node max(Node x){
//判断x还有没有右子结点,如果有,则继续向右查找,如果没有,则x就是最大键所在的结点
if (x.right!=null){
return max(x.right);
}else{
return x;
}
}
P90 89_树_二叉树_遍历概述
P91 90_树_二叉树_前序遍历
ergodic-遍历
//获取整个树中所有的键
public Queue<Key> preErgodic(){
Queue<Key> keys = new Queue<>();
preErgodic(root, keys);
return keys;//获取整个树中所有的键,根据键,可以获得值
}
//获取指定树x的所有键,并放到keys队列中
private void preErgodic(Node x,Queue<Key> keys){
if (x==null){
return;
}
//把x结点的key放入到keys中
keys.enqueue(x.key);
//递归遍历x结点的左子树
if (x.left!=null){
preErgodic(x.left,keys);
}
//递归遍历x结点的右子树
if (x.right!=null){
preErgodic(x.right,keys);
}
}
package test;
import linear.Queue;
import tree.BinaryTree;
public class BinaryTreeErgodicTest {
public static void main(String[] args) {
//创建树对象
BinaryTree<String, String> tree = new BinaryTree<>();
//往树中添加数据
tree.put("E", "5");
tree.put("B", "2");
tree.put("G", "7");
tree.put("A", "1");
tree.put("D", "4");
tree.put("F", "6");
tree.put("H", "8");
tree.put("C", "3");
//遍历
Queue<String> keys = tree.preErgodic();
for (String key : keys) {
String value = tree.get(key);
System.out.println(key+"----"+value);
}
}
}
P92 91_树_二叉树_中序遍历
P93 92_树_二叉树_后序遍历
//使用中序遍历获取树中所有的键
public Queue<Key> midErgodic(){
Queue<Key> keys = new Queue<>();
midErgodic(root,keys);
return keys;
}
//使用中序遍历,获取指定树x中所有的键,并存放到key中
private void midErgodic(Node x,Queue<Key> keys){
if (x==null){
return;
}
//先递归,把左子树中的键放到keys中
if (x.left!=null){
midErgodic(x.left,keys);
}
//把当前结点x的键放到keys中
keys.enqueue(x.key);
//在递归,把右子树中的键放到keys中
if(x.right!=null){
midErgodic(x.right,keys);
}
}
//使用后序遍历,把整个树中所有的键返回
public Queue<Key> afterErgodic(){
Queue<Key> keys = new Queue<>();
afterErgodic(root,keys);
return keys;
}
//使用后序遍历,把指定树x中所有的键放入到keys中
private void afterErgodic(Node x,Queue<Key> keys){
if (x==null){
return ;
}
//通过递归把左子树中所有的键放入到keys中
if (x.left!=null){
afterErgodic(x.left,keys);
}
//通过递归把右子树中所有的键放入到keys中
if (x.right!=null){
afterErgodic(x.right,keys);
}
//把x结点的键放入到keys中
keys.enqueue(x.key);
}
public static void main(String[] args) {
//创建树对象
BinaryTree<String, String> tree = new BinaryTree<>();
//往树中添加数据
tree.put("E", "5");
tree.put("B", "2");
tree.put("G", "7");
tree.put("A", "1");
tree.put("D", "4");
tree.put("F", "6");
tree.put("H", "8");
tree.put("C", "3");
//遍历
Queue<String> keys = tree.afterErgodic();
for (String key : keys) {
String value = tree.get(key);
System.out.println(key+"----"+value);
}
}
P94 93_树_二叉树_层序遍历
层序遍历用到广度优先遍历的思想。
前中后序用到深度优先便利的思想。
//使用层序遍历,获取整个树中所有的键
public Queue<Key> layerErgodic(){
//定义两个队列,分别存储树中的键和树中的结点
Queue<Key> keys = new Queue<>();
Queue<Node> nodes = new Queue<>();
//默认,往队列中放入根结点
nodes.enqueue(root);
while(!nodes.isEmpty()){
//从队列中弹出一个结点,把key放入到keys中
Node n = nodes.dequeue();
keys.enqueue(n.key);
//判断当前结点还有没有左子结点,如果有,则放入到nodes中
if (n.left!=null){
nodes.enqueue(n.left);
}
//判断当前结点还有没有右子结点,如果有,则放入到nodes中
if (n.right!=null){
nodes.enqueue(n.right);
}
}
return keys;
}
P95 94_树_二叉树_最大深度问题
//获取整个树的最大深度
public int maxDepth(){
return maxDepth(root);
}
//获取指定树x的最大深度
private int maxDepth(Node x){
if (x==null){
return 0;
}
//x的最大深度
int max=0;
//左子树的最大深度
int maxL=0;
//右子树的最大深度
int maxR=0;
//计算x结点左子树的最大深度
if (x.left!=null){
maxL = maxDepth(x.left);
}
//计算x结点右子树的最大深度
if (x.right!=null){
maxR = maxDepth(x.right);
}
//比较左子树最大深度和右子树最大深度,取较大值+1即可
max = maxL>maxR?maxL+1:maxR+1;
return max;
}
package test;
import tree.BinaryTree;
public class BinaryTreeMaxDepthTest {
public static void main(String[] args) {
//创建树对象
BinaryTree<String, String> tree = new BinaryTree<>();
//往树中添加数据
tree.put("E", "5");
tree.put("B", "2");
tree.put("G", "7");
tree.put("A", "1");
tree.put("D", "4");
tree.put("F", "6");
tree.put("H", "8");
tree.put("C", "3");
int maxDepth = tree.maxDepth();
System.out.println(maxDepth);
}
}
P96 95_树_二叉树_折纸问题
利用层序遍历的思想。