树数据结构
文章目录
1.定义:
由n个结点组成的一个具有层次关系的集合。
2.图解:
①每个结点由零个或多个子结点;
②无父结点的结点叫做根结点;
③每一个非根节点都只有一个父结点。
3.基本概念
①结点的度:一个结点含有的子树个数;
②叶结点:度为0的结点;
③分支结点:度不为0的结点;
④层次:从根结点开始,层次为1,后续为n(n>=2);
⑤树的度:树中最大的度;
⑥树的高度(深度):树中结点的最大层次;
⑦森林:去掉根结点的树;
⑧孩子结点:一个结点的直接后继结点;
⑨父结点,兄弟节点与⑧类似。
4.二叉树
(1)定义:度不超过2的树。
(2)图解:
(3)二叉树树的设计框架:
BinaryTree <Key extends Comparable<Key>Value value>
可实现根据Key去排序,继承Comparable类。
(4)代码实现:
<1>属性与构造方法
//根结点
private Node root;
//元素个数
private int N;
//构造方法
public BinaryTree_二叉树(){
root = null;
N = 0;
}
<2>内部结点类Node
//内部结点类Node
private class Node {
//键
Key key;
//值
Value value;
//左结点
Node left;
//右结点
Node right;
//构造方法
public Node(Key key,Value value,Node left,Node right){
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
}
<3>功能方法
a.插入键值对
//插入一个键值对方法
public void put(Key key,Value value){
put(root,key,value);
}
//在指定树x插入一个键值对方法
public Node put(Node x,Key key,Value value){
/*
当树x为空时,进入结束条件
①创建出一个新结点,返回新结点即可
②元素个数+1
* */
if (x == null){
N++;
Node newNode = new Node(key,value,null,null);
return newNode;
}
/*
* 当树x不为空时,需继续向下遍历:
* ①比较Key值大小,若Key的值较大,进入x的右结点
* 若Key的值较小,进入x的左结点
* 若Key的值与原来相同,则直接覆盖原来的Value;
* ②元素个数+1
* */
if (key.compareTo(x.key)>0){
x.right = put(x.right,key,value);
}
else if (key.compareTo(x.key)<0){
x.left = put(x.left,key,value);
}
else {
x.value = value;
}
//该return无实际作用。
return null;
}
b.根据Key查找Value
//根据Key查找Value
public Value get(Key key){
return get(root,key);
}
//根据Key在树x中查找Value
public Value get(Node x,Key key){
//若树x为空,返回null即可
if (x == null) {
return null;
}
/*
* 若树x不为空,比较树x的key与key值大小:
* ①当key的值比x树上的key大时,进入x的右结点
* ②当key的值比x树上的key小时,进入x的左结点
* ③当key的值与x树上的key相等时,返回x的Value即可
* */
if (key.compareTo(x.key)>0){
return get(x.right,key);
}else if (key.compareTo(x.key)<0){
return get(x.left,key);
}else{
return x.value;
}
}
c.删除键值对(难)
//删除树中的键值对
public void delete(Key key){
delete(root,key);
}
//删除指定树x中的键值对
public Node delete(Node x,Key key){
//当树x为空时,返回null即可
if (x == null){
return null;
}
/*
* (1)当树x不为空,比较key与树x的key:
* ①当key值比x树上的key大,进入x的右子树
* ②当key值比x树上的key小,进入x的左子树
* ③当key值与x树上的key值相等,进入删除操作;
* (2)删除操作;
* (3)元素个数-1。
* */
//如果当前结点不为空:
if (key.compareTo(x.key)>0){
//向当前节点的右子树走:
x.right = delete(x.right,key);
}else if (key.compareTo(x.key)<0){
//向当前节点的左子树走:
x.left = delete(x.left,key);
}else{//找到原树中键是key的结点:
/*
* 删除结点的思路:
*1.找到要删除元素的右子树中的最小值:
* */
if (x.right == null){
return x.left;
}
if (x.left == null){
return x.right;
}
//若以上条件均不满足,则找右子树中的最小值:
Node minNode = x.right;
while (minNode.left != null){
//如果最小结点仍有左节点,则向下遍历;
minNode = minNode.left;
}
//删除右子树中最小的结点:
Node n = x.right;
while (n.left != null){
if (n.left.left == null){
n.left = null;
}else{
n = n.left;
}
}
//让x结点的左子树成为minNode的左子树
minNode.left = x.left;
//让x结点的右子树成为minNode的右子树
minNode.right = x.right;
//让x结点的父结点指向minNode。
x = minNode;
}
//元素个数-1:
N--;
return x;
}
d.获取元素个数
//获取元素个数
public int size(){
return N;
}
e.获取树中最小值的键
//获取树中最小值的键
public Key min(){
return min(root).key;
}
//获取指定树中x的最小值的结点
public Node min(Node x){
//当树x没有左结点时,自己本身就是最小值
if (x.left == null){
return x;
}
else{
return min(x.left);
}
}
f.获取树中最大值的键
//获取树中最大值的键
public Key max(){
return max(root).key;
}
//获取指定树中x的最大值的结点
public Node max(Node x){
//当树x不为空时,从树x开始,一直向右递归遍历即可得到最大结点。
if (x.right == null){
return x;
}else{
return max(x.right);
}
}
<4>二叉树几种遍历方法
对二叉树:
前序遍历:
①顺序:1->2->3
②结果:ABDECFG
中序遍历:
①顺序:2->1->3
②结果:DBEACFG
后序遍历:
①顺序:2->3->1
②结果:DEBFGCA
层序遍历:
a.前序遍历
A.思路分析:
A.当树x为空时,无需遍历,直接返回即可
B.当树x不为空时,递归调用该方法将key存入keys中即可。
由于是前序遍历(当前结点应先输出):
①将当前结点x存入队列中,优先输出;
②如果x有左子树,递归将左子树传入即可;
③如果x有右子树,递归将右子树传入即可。
B.代码实现:
//获取整个树中的所有键
public Queue<Key> preErgodic(){
//创建一个新队列,调用获取root树上的所有键方法,返回队列即可。
Queue<Key> keys = new Queue<>();
preErgodic(root,keys);
return keys;
}
//获取指定树x中的所有键,并存入keys队列中
public void preErgodic(Node x,Queue<Key> keys){
/*
A.当树x为空时,无需遍历,直接返回即可
B.当树x不为空时,递归调用该方法将key存入keys中即可。
前序遍历:①将当前结点x存入队列中,优先输出;
②如果x有左子树,递归将左子树传入即可;
③如果x有右子树,递归将右子树传入即可。
*/
if (x == null){
return;
}
//将当前结点x的key存入队列keys中,优先输出
keys.insert(x.key);
//左子树遍历
if (x.left != null){
preErgodic(x.left,keys);
}
//右子树遍历
if (x.right != null){
preErgodic(x.right,keys);
}
}
b.中序遍历
A.思路分析:
A.当树x为空时,无需遍历,直接返回即可
B.当树x不为空时,递归调用该方法将key存入keys中即可。
由于是中序遍历(当前结点应在中间输出):
①如果x有左子树,递归将左子树传入即可;
②将当前结点x存入队列中,中间输出;
③如果x有右子树,递归将右子树传入即可。
B.代码实现:
//获取整个树中的键
public Queue <Key> midErgodic() {
Queue<Key> keys = new Queue<>();
midErgodic(root,keys);
return keys;
}
//获取指定树x上的键,并将其存入队列keys中
public void midErgodic(Node x,Queue<Key> keys){
//当当前树x为空时,直接返回即可
if (x == null){
return ;
}
//左子树存入队列
if (x.left != null){
midErgodic(x.left,keys);
}
//当前结点x存入队列中
keys.insert(x.key);
//右子树存入队列
if (x.right != null){
midErgodic(x.right,keys);
}
}
c.后序遍历
A.思路分析:
A.当树x为空时,无需遍历,直接返回即可
B.当树x不为空时,递归调用该方法将key存入keys中即可。
由于是后序遍历(当前结点应在最后输出):
①如果x有左子树,递归将左子树传入即可;
②如果x有右子树,递归将右子树传入即可。
③将当前结点x存入队列中,最后输出;
B.代码实现:
//获取整个树中的键
public Queue<Key> afterErgodic(){
Queue<Key> keys = new Queue<>();
afterErgodic(root,keys);
return keys;
}
//获取指定树x上的键
public void afterErgodic(Node x,Queue<Key> keys){
//当当前树x为空时,直接返回
if (x == null){
return;
}
//遍历左子树
if (x.left != null){
afterErgodic(x.left,keys);
}
//遍历右子树
if (x.right != null){
afterErgodic(x.right,keys);
}
//将当前节点x存入keys
keys.insert(x.key);
}
d.层序遍历:
A.思路分析:
①创建两个队列,分别存储弹出的Node和Key;
②默认将根结点传入Node所在的队列中;
③循环遍历:当结点队列没有元素时停止遍历,每循环一次从结点队列中弹出一个元素,将其key存入keys队列中。
④弹出结点如果有左(右)结点,则继续向结点队列中插入即可。
B.代码实现
//层序遍历——二叉树的高级遍历
public Queue<Key> layerErgodic(){
//创建两个队列,分别存储弹出元素的key和node:
Queue<Key> keys = new Queue <>();
Queue<Node> nodes = new Queue <>();
//默认将根节点放入nodes中:
nodes.insert(root);
//循环遍历,当队列中没有元素时停止循环:
while(!nodes.isEmply()){
//从队列中弹出一个结点,将key放入keys中:
Node removeElement = nodes.remove();
keys.insert(removeElement.key);
//判断元素如果有左子树,将左子树进入队列:
if (removeElement.left!=null){
nodes.insert(removeElement.left);
}
//判断元素如果有右子树,将右子树进入队列:
if (removeElement.right!=null){
nodes.insert(removeElement.right);
}
}
return keys;
}
(5)二叉树的简单应用
<1>求二叉树的最大深度:
a.思路分析:
①如果树无元素,直接返回0即可;
②如果树有元素,则分别求左右子结点的最大深度;
③比较左右子结点最大深度,取较大值即为二叉树的最大深度。
b.代码实现:
//求整棵树的最大深度
public int maxDepth(){
return maxDepth(root);
}
//求指定树x的最大深度
public int maxDepth(Node x){
//如果当前树x为空,直接返回0
if (x == null){
return 0;
}
//不为空则分别遍历左右两结点
int leftDepth = maxDepth(x.left)+1;
int rightDepth = maxDepth(x.right)+1;
//返回左右两边较大值即可
return leftDepth > rightDepth ? leftDepth : rightDepth;
}
完!