Java 数据结构_树
本文由 Luzhuo 编写,转发请保留该信息.
原文: http://blog.csdn.net/Rozol/article/details/78469973
树
树的基本概念
- 树是结点的有限集合
- 双亲: 根结点A是BCD的
双亲
, 下面的BCD都是A的孩子
- 兄弟: 同一个双亲之间的结点, 互称为
兄弟
, BCD互为兄弟 - 堂兄弟: 双亲在同一层的结点, 互称为
堂兄弟
, EFGHIJKL互为堂兄弟 - 度: 连接他的结点数, A的度为3
- 根结点: 无双亲, 且唯一的结点
- 分支结点(非终端结点): 度不为0的结点, BCD都是分支结点, 也称中间结点(除A外)
- 叶结点(终端结点): 度为0的结点, EFGHIJKL都是叶结点.
- 祖先: 之前所有的结点都是祖先, G的祖先是BA
- 子孙: 之后所有的结点都是子孙, B的子孙是EFG
- 有序树(从左到右不能换顺序), 无序树(可换顺序)
- 深度: 树的最大深度, 这颗树的深度为3
- 森林: 多个独立的树(互不相交)放在一起就是森林
- 二叉树: 每个结点的度最多为2的树
- 树的遍历:
- 用途:压缩软件-赫夫曼树 / 搜索-人机对战
二叉树的实现方式
数组实现方式, 删除指定索引位置的结点时, 不会删除他的所有孩子
链式实现方式, 删除指定结点时, 会删除他的所有孩子
二叉树的数组实现方式
二叉树的数组实现代码
/**
* 二叉树(数组的表示方式)
* @author Luzhuo
* A(0)
* B(1) C(2)
* D(3) E(4) F(5) G(6)
*
* |A(0)| B(1)|C(2) | D(3)|E(4) | F(5)|G(6) | ...|... |
*/
public class ArrayTree {
// 树的数组表示方式
private Node[] nodes; // 树的指针
private int size; // 数组的大小
public ArrayTree(Node root){
init(1024, root);
}
public ArrayTree(int size, Node root){
init(size, root);
}
/**
* 创建二叉树<br>
* 创建存放结点的数组, 设置该数据结构的容量, 设置根结点
* @param size
* @param root
*/
private void init(int size, Node root){
this.nodes = new Node[size];
this.size = size;
// 根结点
this.nodes[0] = root;
}
/**
* 销毁二叉树<br>
* 删除存放结点的数组
*/
public void destory(){
nodes = null;
}
/**
* 根据索引搜索结点
* @param nodeIndex 索引
* @return 结点
*/
public Node serchNode(int nodeIndex){
if(nodeIndex < 0 || nodeIndex >= this.size){
throw new ArrayIndexOutOfBoundsException("索引越界.");
}
return nodes[nodeIndex];
}
/**
* 添加结点<br>
* 添加在父结点的左边时, 添加到数组的奇数索引位置(1, 3, 5 ...), 添加在父结点的右边时, 添加到数组的偶数索引位置(2, 4, 6 ...)
* @param nodeIndex 结点索引(从0开始)
* @param direction 左边0还是右边1
* @param node 结点
* @return 是否添加成功
*/
public boolean addNode(int nodeIndex, int direction, Node node){
if(nodeIndex < 0 || nodeIndex >= this.size){
throw new ArrayIndexOutOfBoundsException("索引越界.");
}
if(this.nodes[nodeIndex] == null){
throw new NullPointerException("该结点为空");
}
// 若某结点再父节点下标 * 2 + 1 ,则表示该结点在左
// 若某结点再父节点下标 * 2 + 2, 则表示该结点在右
int leftIndex = nodeIndex * 2 + 1;
int rightIndex = nodeIndex * 2 + 2;
if(direction == 0){
if(leftIndex >= this.size || this.nodes[leftIndex] != null) return false;
this.nodes[leftIndex] = node;
}else if(direction == 1){
if(rightIndex >= this.size || this.nodes[rightIndex] != null) return false;
this.nodes[rightIndex] = node;
}
return true;
}
/**
* 删除索引<br>
* @param nodeIndex
* @return 被删除的结点
*/
public Node deleteNode(int nodeIndex){
if(nodeIndex < 0 || nodeIndex >= this.size){
throw new ArrayIndexOutOfBoundsException("索引越界.");
}
if(this.nodes[nodeIndex] == null){
throw new NullPointerException("该结点为空");
}
Node tempN = this.nodes[nodeIndex];
this.nodes[nodeIndex] = null;
return tempN;
}
/**
* 遍历<br>
* 之间从左向右遍历数组.
*/
public void traverse(){
for(int i = 0; i < this.size; i++){
Node data = this.nodes[i];
if(data != null) System.out.print(data + " ");
}
}
}
class Node{
public char data;
public Node(char data){
this.data = data;
}
@Override
public String toString() {
return String.valueOf(data);
}
}
数组方式测试代码
public class ArrayTreeTest {
public static void main(String[] args) {
// 测试
ArrayTree tree = new ArrayTree(new Node('A'));
// tree.addNode(0, 0, new Node('B'));
// tree.addNode(0, 1, new Node('C'));
// tree.addNode(1, 0, new Node('D'));
// tree.addNode(1, 1, new Node('E'));
// tree.addNode(2, 0, new Node('F'));
// tree.addNode(2, 1, new Node('G'));
// ↓ 简化
int nodeIndex = -1;
for(int i = 0; i < 10; i++){
int direction;
if(i%2 == 0){
direction = 0;
nodeIndex++;
}else{
direction = 1;
}
tree.addNode(nodeIndex, direction, new Node((char)(66+i)));
}
tree.traverse();
System.out.println();
System.out.println(tree.serchNode(3));
System.out.println(tree.deleteNode(3));
tree.traverse();
tree.destory();
}
}
二叉树的链式实现方式
二叉树的链式实现代码
/**
* 二叉树(链式实现方式)
* @author Luzhuo
* (0)
* A(1) B(2)
* C(3) D(4) E(5) F(6)
* 前序遍历(根左右): 0 1 3 4 2 5 6
* 中序遍历(左根右): 3 1 4 0 5 2 6
* 后序遍历(左右根): 3 4 1 5 6 2 0
*/
public class LinkedTree {
Node_ root; // 根结点
public LinkedTree(){
init();
}
/**
* 创建链式二叉树<br>
* 初始化根结点的数据0, 索引0, 左孩子指针null, 右孩子指针null, 父节点指针null
*/
private void init(){
root = new Node_();
root.data = 0;
root.index = 0;
root.lChildNode = null;
root.rChildNode = null;
root.parentNode = null;
}
/**
* 销毁链式二叉树<br>
* 递归删除所有孩子, 并指针重置为null
*/
public void destory(){
this.root.deleteNode();
this.root = null;
}
/**
* 根据索引寻找结点<br>
* 递归查找指定索引位置的结点
* @param nodeindex 索引
* @return 结点
*/
public Node_ searchNode(int nodeindex){
return this.root.searchNode(nodeindex);
}
/**
* 添加结点<br>
* 找到指定位置的结点, 将新结点添加到该结点的左边 / 右边, 并将该结点的指针交给新结点
* @param nodeindex 索引
* @param direction 左边0或右边1
* @param node 被挂在的结点
* @return 添加成功返回true,否则返回false;
*/
public boolean addNode(int nodeindex, int direction, Node_ node){
Node_ curN = searchNode(nodeindex);
if(curN == null) return false;
// 加入到该结点的左边or右边
if(direction == 0){
curN.lChildNode = node;
}else if(direction == 1){
curN.rChildNode = node;
}
// 并该结点的指针交给插入的结点
node.parentNode = curN;
return true;
}
/**
* 删除指定索引的结点, 注意删除结点,会将挂在在该结点的所有结点同时删除<br>
* 找到指定的结点, 递归删除自己所有的孩子
* @param nodeindex 索引
* @return 被删除的结点, 该结点为空返回null
*/
public Node_ deleteNode(int nodeindex){
Node_ curN = searchNode(nodeindex);
if(curN == null) return null;
Node_ tempN = curN;
curN.deleteNode();
curN = null;
return tempN;
}
/**
* 前序遍历(根左右, 根前)
*/
public void preorderTraversal(){
this.root.preorderTraversal();
}
/**
* 中序遍历(左根右, 根中)
*/
public void inorderTraversal(){
this.root.inorderTraversal();
}
/**
* 后序遍历(左右根, 根后)
*/
public void postorderTraversal(){
this.root.postorderTraversal();
}
}
/**
* 结点, 创建必须给index和data赋值
* @author Luzhuo
*
*/
class Node_{
public int index; // 索引
public char data; // 数据
public Node_ lChildNode; // 左结点指针
public Node_ rChildNode; // 右结点指针
public Node_ parentNode; // 父结点指针
public Node_(){ }
public Node_(int index, char data){
this.index = index;
this.data = data;
}
/**
* 根据索引寻找结点
* @param nodeindex 索引
* @return 找到结点返回结点, 否则返回null
*/
public Node_ searchNode(int nodeindex){
// 是否是自身
if(this.index == nodeindex) return this;
Node_ tempN;
// 是否是左边的孩子,及其子结点
if(this.lChildNode != null){
if(this.lChildNode.index == nodeindex){
return this.lChildNode;
}else{
tempN = this.lChildNode.searchNode(nodeindex);
if(tempN != null) return tempN;
}
}
// 是否是右边的孩子,及其子结点
if(this.rChildNode != null){
if(this.rChildNode.index == nodeindex){
return this.rChildNode;
}else{
tempN = this.rChildNode.searchNode(nodeindex);
if(tempN != null) return tempN;
}
}
return null;
}
public void deleteNode(){
// 删除自己左右边的所有子结点
if(this.lChildNode != null){
this.lChildNode.deleteNode();
}
if(this.rChildNode != null){
this.rChildNode.deleteNode();
}
// 删除自己在父结点上的指针
if(this.parentNode != null){
if(this.parentNode.lChildNode == this){
this.parentNode.lChildNode = null;
}
if(this.parentNode.rChildNode == this){
this.parentNode.rChildNode = null;
}
}
}
/**
* 前序遍历(根左右)<br>
* 每个结点先输出自身(根), 然后递归左结点, 在递归右结点
*/
public void preorderTraversal(){
// 根
System.out.println("index: " + this.index + " data: " + data + " === ");
// 左
if(this.lChildNode != null) this.lChildNode.preorderTraversal();
// 右
if(this.rChildNode != null) this.rChildNode.preorderTraversal();
}
/**
* 中序遍历(左根右)
*/
public void inorderTraversal(){
// 左
if(this.lChildNode != null) this.lChildNode.inorderTraversal();
// 根
System.out.println("index: " + this.index + " data: " + data + " === ");
// 右
if(this.rChildNode != null) this.rChildNode.inorderTraversal();
}
/**
* 后序遍历(左右根)
*/
public void postorderTraversal(){
// 左
if(this.lChildNode != null) this.lChildNode.postorderTraversal();
// 右
if(this.rChildNode != null) this.rChildNode.postorderTraversal();
// 根
System.out.println("index: " + this.index + " data: " + data + " === ");
}
}
数组方式测试代码
public class LinkedTreeTest {
public static void main(String[] args) {
// 测试
/*
* (0)
* A(1) B(2)
* C(3) D(4) E(5) F(6)
* 前序遍历(根左右): 0 1 3 4 2 5 6
* 中序遍历(左根右): 3 1 4 0 5 2 6
* 后序遍历(左右根): 3 4 1 5 6 2 0
*/
LinkedTree tree = new LinkedTree();
// tree.addNode(0, 0, new Node_(1, 'A'));
// tree.addNode(0, 1, new Node_(2, 'B'));
// tree.addNode(1, 0, new Node_(3, 'C'));
// tree.addNode(1, 1, new Node_(4, 'D'));
// tree.addNode(2, 0, new Node_(5, 'E'));
// tree.addNode(2, 1, new Node_(6, 'F'));
// ↓ 简化
int nodeIndex = -1;
for(int i = 0; i < 10; i++){
int direction;
if(i%2 == 0){
direction = 0;
nodeIndex++;
}else{
direction = 1;
}
tree.addNode(nodeIndex, direction, new Node_(i + 1, (char)(65+i)));
}
tree.preorderTraversal();
System.out.println();
tree.inorderTraversal();
System.out.println();
tree.postorderTraversal();
System.out.println();
System.out.println(tree.deleteNode(3).data);
System.out.println(tree.deleteNode(2).data);
tree.preorderTraversal();
tree.destory();
}
}