思想来源
在现实的项目开发中,我们经常会用到树形数据结构
,对于什么是树形数据结构,具体可以参考如下的两篇博文:
在看完上面两篇博文的基础上,我们就对树形数据结构,尤其是对ClosureTable有了更加清晰的认识,既然项目中经常会用到该树形数据结构,那么我们就有必要手写一个实现版本。
源码
文件目录结构
项目源码
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.lyc</groupId>
<artifactId>closure-table</artifactId>
<version>1.0-SNAPSHOT</version>
<name>closure-table</name>
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- SpringCloud版本 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- SpringBoot Web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- lombok依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
AncestorSequence
package com.lyc.closure.closureTable.core.ancestor;
import com.lyc.closure.closureTable.core.Node;
import com.lyc.closure.closureTable.core.entity.NodeArray;
/**
* @author: 张侦毅
* @date: 2019/6/16 9:58
* @description: 祖先接口
**/
public interface AncestorSequence {
// 节点数组指针的初始化
boolean init(Node ele);
// 加载节点数组指针域
boolean loadNodeArray(Node ele);
// 获取NodeArray对象
NodeArray getNodeArray();
// 获取当前节点数组
Node[] getCurrentNodeArray();
// 获取目标节点中的节点数组
Node[] getNodeArray(Node ele);
// 拷贝目标节点中的节点数组
Node[] copyNodeArray(Node ele);
// 设置节点中的节点数组指针域
boolean setNodeArray(Node[] nodeArray, Node ele);
// 写入节点中的节点数组指针域
boolean writeNodeArray(Node ele);
// 节点数组指针的判空
boolean isEmpty();
// 获取节点数组指针的长度
int length();
// 获取节点数组指针指定位置处的元素
boolean getEle(int index, Node ele);
// 返回第一个满足ele的数据元素的位置
int locateEle(Node data);
// 获取节点数组指针指定元素的前驱
boolean priorEle(Node currentEle, Node preEle);
// 获取节点数组指针指定元素的后继
boolean nextEle(Node currentEle, Node nextEle);
// 节点数组指针的数据添加(在最后添加)
boolean insert(Node ele);
// 节点数组指针的数据添加
boolean insert(int index, Node ele);
// 节点数组指针的数据删除(删除尾结点)
boolean delete();
// 节点数组指针的数据删除
boolean delete(int index, Node pNode);
// 节点数组指针的数据遍历
void traverse();
// 节点数组扩容
boolean dilatation();
// 节点数据深拷贝
boolean cope(Node current, Node target);
}
AncestorSequenceImpl
package com.lyc.closure.closureTable.core.ancestor;
import com.lyc.closure.closureTable.core.Node;
import com.lyc.closure.common.ClosureCommonConstant;
import com.lyc.closure.closureTable.core.entity.NodeArray;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import java.util.Arrays;
/**
* @author: 张侦毅
* @date: 2019/6/16 9:59
* @description: 祖先接口实现类
**/
@Slf4j
@Getter
@Setter
@NoArgsConstructor
public class AncestorSequenceImpl implements AncestorSequence{
// 节点数组指针域
private NodeArray nodeArray = null;
// 初始化节点数组
private static final Node[] INIT_NODE_ARRAY = new Node[ClosureCommonConstant.Closure.INIT_CAPACITY];
/**
* 节点数组指针的初始化
* @param ele 节点
* @return boolean
*/
@Override
public boolean init(Node ele) {
// 如果节点不为空,则初始化节点数组,同时初始化节点的节点数组指针域
if(ele != null){
// 创建父辈路径节点数组
NodeArray ancestors = NodeArray.builder()
.size(ClosureCommonConstant.Closure.INIT_CAPACITY)
.pList(INIT_NODE_ARRAY)
.iLength(ClosureCommonConstant.Closure.INIT_DATA_LENGTH)
.build();
// 将创建的父辈路径节点数组添加到父辈节点域中
ele.setAncestors(ancestors);
// 加载Node对象中的NodeArray对象到NodeArrayCoreImpl的nodeArray中
loadNodeArray(ele);
return true;
}
return false;
}
/**
* 加载节点数组指针域,如果成功,则返回true,失败则返回false
* @param ele 目标节点
* @return boolean
*/
@Override
public boolean loadNodeArray(Node ele) {
if(ele != null){
this.nodeArray = ele.getAncestors();
return true;
}
return false;
}
/**
* 获取当前节点数组
* @return Node[]
*/
@Override
public Node[] getCurrentNodeArray() {
return this.nodeArray.getPList();
}
/**
* 获取目标节点中的节点数组
* @param ele 目标节点
* @return Node[]
*/
@Override
public Node[] getNodeArray(Node ele) {
// 如果节点不为空,则返回节点中的节点数组指针域
if(ele != null){
return ele.getAncestors().getPList();
}
return null;
}
/**
* 拷贝目标节点中的节点数组
* @param ele 目标节点
* @return Node[]
*/
@Override
public Node[] copyNodeArray(Node ele) {
// 如果节点不为空,则返回节点中的节点数组指针域
if(ele != null){
// 获取NodeArray容器
Node[] nodeArrayContainer = ele.getAncestors().getPList();
// 从容器中复制有效数据,并返回
return Arrays.copyOf(nodeArrayContainer,ele.getAncestors().getILength());
}
return null;
}
/**
* 设置节点中的节点数组指针域
* @param nodeArray 节点数组指针域
* @param ele 目标节点
* @return boolean
*/
@Override
public boolean setNodeArray(Node[] nodeArray,Node ele) {
// 如果当前节点数组指针域与目标节点均不为空,则执行赋值操作
if(nodeArray != null && ele != null){
ele.getAncestors().setPList(nodeArray);
return true;
}
return false;
}
/**
* 写入节点中的节点数组指针域
* @param ele 节点数组指针域
* @return boolean
*/
@Override
public boolean writeNodeArray(Node ele) {
// 如果不为空,则将节点数组指针域回写到当前节点中
if(ele != null){
ele.setAncestors(this.getNodeArray());
return true;
}
return false;
}
/**
* 节点数组指针的判空
* @return boolean
*/
@Override
public boolean isEmpty() {
if(this.nodeArray != null && this.nodeArray.getILength() == 0){
return true;
}
return false;
}
/**
* 获取节点数组指针的长度
* @return int
*/
@Override
public int length() {
if(this.nodeArray != null){
return this.nodeArray.getILength();
}
return 0;
}
/**
* 获取节点数组指针指定位置处的元素
* @param index 节点的索引值
* @param ele 获取的元素
* @return boolean
*/
@Override
public boolean getEle(int index, Node ele) {
// 如果数据在有效范围内
if(index >= 0 && index < this.nodeArray.getILength()){
// 则取出相应的元素直接赋值给ele,传出
// 获取目标节点
Node temp = this.nodeArray.getPList()[index];
// 数据拷贝
cope(temp,ele);
// 获取成功后返回成功状态
return true;
}
return false;
}
/**
* 返回第一个满足ele的数据元素的位序
* @param ele 目标节点
* @return int
*/
@Override
public int locateEle(Node ele) {
// 如果目标节点存在
if(ele != null){
// 获取节点数组指针域
Node[] pList = this.nodeArray.getPList();
// 获取节点数组的长度
int length = this.nodeArray.getILength();
// 循环遍历,依次查找比对节点的数据域
for(int i = 0; i < length; i ++){
// 如果当前节点的数据域与目标节点的数据域相同,则返回当前节点所在的位序
if(pList[i].getData() == ele.getData()){
return i;
}
}
}
// 如果查找不到,则返回位序值为-1
return -1;
}
/**
* 获取节点数组指针指定元素的前驱
* @param currentEle 当前节点
* @param preEle 前一个节点
* @return boolean
*/
@Override
public boolean priorEle(Node currentEle, Node preEle) {
// 获取当前元素所在的位序
int temp = locateEle(currentEle);
// 如果在有效范围内
if(temp > 0){
// 第一个元素没有前驱
// 获取前驱节点
Node temp2 = this.nodeArray.getPList()[temp - 1];
// 数据拷贝
cope(temp2,preEle);
// 前驱节点获取成功后返回true
return true;
}
return false;
}
/**
* 获取节点数组指针指定元素的后继
* @param currentEle 当前元素
* @param nextEle 指定元素的后继
* @return boolean
*/
@Override
public boolean nextEle(Node currentEle, Node nextEle) {
// 获取当前元素所在的位序
int temp = locateEle(currentEle);
// 如果在有效范围内
if(temp != -1 && temp < this.nodeArray.getILength() -1){
// 获取后继节点
Node temp2 = this.nodeArray.getPList()[temp + 1];
// 数据拷贝
cope(temp2,nextEle);
// 获取后继点解成功后返回true
return true;
}
return false;
}
/**
* 节点数组指针的数据添加(在最后添加)
* @param ele 目标元素
* @return boolean
*/
@Override
public boolean insert(Node ele) {
// 获取节点数组的长度
int length = length();
// 如果数据在有效范围内
if(length < this.nodeArray.getSize()){
// 运行扩容方法(不一定会扩容)
dilatation();
// 将传入节点插入到节点数组中(尾端插入)
this.nodeArray.getPList()[length] = ele;
// 添加完成之后,长度自增1
this.nodeArray.setILength(this.nodeArray.getILength() + 1);
return true;
}
// 执行插入操作
return false;
}
/**
* 节点数组指针的数据添加
* @param index 索引值
* @param ele 目标元素
* @return boolean
*/
@Override
public boolean insert(int index, Node ele) {
// 如果插入点在有效范围内
if(index > 0 && index < this.nodeArray.getSize() - 1){
// 运行扩容方法(不一定会扩容)
dilatation();
// 将传入节点插入到节点数组中(非尾端插入,需要移动节点)
// 先获取节点数组
Node[] pList = this.nodeArray.getPList();
for(int k = this.nodeArray.getILength() - 1; k >= index; k --){
// 把当前元素向后移动一位
pList[k + 1] = pList[k];
}
// 将目标元素插入到目标位序
pList[index] = ele;
// 将数据长度+1
this.nodeArray.setILength(this.nodeArray.getILength() + 1);
return true;
}
return false;
}
/**
* 节点数组指针的数据删除(删除尾结点)
* @return boolean
*/
@Override
public boolean delete() {
// 如果数据在有效长度范围内
if(this.nodeArray.getILength() > 0){
// 保证数组容器内至少有一个节点
// 删除尾结点(采用逻辑删除即可)
this.nodeArray.setILength(this.nodeArray.getILength() - 1);
}
return false;
}
/**
* 节点数组指针的数据删除
* @param index 目标节点位序
* @param pNode 目标节点
* @return boolean
*/
@Override
public boolean delete(int index, Node pNode) {
// 如果数据在有效范围内
if(index > -1 && index <= this.nodeArray.getILength() - 1){
// 获取节点数组
Node[] pList = this.nodeArray.getPList();
// 获取指定位置处的节点
getEle(index,pNode);
// 循环遍历,向左移动数组
for(int k = index + 1; k < this.nodeArray.getILength(); k ++){
// 后一个元素向左移动一个元素
pList[index] = pList[k];
}
// 节点数组长度自减1
this.nodeArray.setILength(this.nodeArray.getILength() - 1);
return true;
}
return false;
}
/**
* 节点数组指针的数据遍历
*/
@Override
public void traverse() {
// 获取节点数组
Node[] pList = this.nodeArray.getPList();
// 获取节点数组长度
int length = this.nodeArray.getILength();
// 如果节点数组不为空
if(length > 0){
// 循环遍历所有的数据
for(int i = 0; i < length; i ++) {
// 在控制台中打印节点数组
log.info(pList[i].getData().toString());
}
}
}
/**
* 节点数组扩容
* @return boolean
*/
@Override
public boolean dilatation() {
// 如果达到加载因子上限,则执行扩容操作
if(this.nodeArray.getILength() >= (this.nodeArray.getSize() * ClosureCommonConstant.Closure.DEFAULT_LOAD_FACTOR)){
// 生成新的容器容量
int capacity = this.nodeArray.getSize() << 1;
// 生成新的节点数组
Node[] pList = Arrays.copyOf(this.nodeArray.getPList(),capacity);
// 将新的节点数组回写到当前节点的节点数组指针域中
this.nodeArray.setPList(pList);
// 将容器的容量设置为新的数值
this.nodeArray.setSize(capacity);
// 扩容成功后,返回true
return true;
}
return false;
}
/**
* 节点数据深拷贝
* @param current 当前节点
* @param target 目标节点
* @return boolean
*/
@Override
public boolean cope(Node current, Node target) {
if(current != null && target != null){
// 执行数据拷贝
BeanUtils.copyProperties(current,target)<