R树的Java实现

以下为R树插入和删除Java代码实现,共7个类文件

package com.njupt.rtree;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
 
import com.njupt.constants.Constants;
 
/**
 * @ClassName RTree 
 * @Description 
 */
public class RTree 
{
	/**
	 * 根节点
	 */
	private RTNode root;
	
	/**
	 * 树类型
	 */
	private int tree_type;
	
	/**
	 * 结点容量
	 */
	private int nodeCapacity = -1;
	
	/**
	 * 结点填充因子
	 */
	private float fillFactor = -1;
	
	private int dimension ;
	
	public RTree(int capacity, float fillFactor, int type, int dimension)
	{
		this.fillFactor = fillFactor;
		tree_type = type;
		nodeCapacity = capacity;
		this.dimension = dimension;
		root = new RTDataNode(this,Constants.NULL);
	}
	
	/**
	 * @return RTree的维度
	 */
	public int getDimension()
	{
		return dimension;
	}
	
	public void setRoot(RTNode root)
	{
		this.root = root;
	}
	
	public float getFillFactor()
	{
		return fillFactor;
	}
	
	/**
	 * @return 返回结点容量
	 */
	public int getNodeCapacity()
	{
		return nodeCapacity;
	}
	
	/**
	 * @return 返回树的类型
	 */
	public int getTreeType()
	{
		return tree_type;
	}
	
	/**
	 * 向Rtree中插入Rectangle<p>
	 * 1、先找到合适的叶节点 <br>
	 * 2、再向此叶节点中插入<br>
	 * @param rectangle
	 */
	public boolean insert(Rectangle rectangle)
	{
		if(rectangle == null)
			throw new IllegalArgumentException("Rectangle cannot be null.");
		
		if(rectangle.getHigh().getDimension() != getDimension())
		{
			throw new IllegalArgumentException("Rectangle dimension different than RTree dimension.");
		}
		
		RTDataNode leaf = root.chooseLeaf(rectangle);
		
		return leaf.insert(rectangle);
	}
	
	/**
	 * 从R树中删除Rectangle <p>
	 * 1、寻找包含记录的结点--调用算法findLeaf()来定位包含此记录的叶子结点L,如果没有找到则算法终止。<br>
	 * 2、删除记录--将找到的叶子结点L中的此记录删除<br>
	 * 3、调用算法condenseTree<br>
	 * @param rectangle
	 * @return
	 */
	public int delete(Rectangle rectangle)
	{
		if(rectangle == null)
		{
			throw new IllegalArgumentException("Rectangle cannot be null.");
		}
		
		if(rectangle.getHigh().getDimension() != getDimension())
		{
			throw new IllegalArgumentException("Rectangle dimension different than RTree dimension.");
		}
		
		RTDataNode leaf = root.findLeaf(rectangle);
		
		if(leaf != null)
		{
			return leaf.delete(rectangle);
		}
		
		return -1;
	}
	
	/**
	 * 从给定的结点root开始遍历所有的结点
	 * @param node
	 * @return 所有遍历的结点集合
	 */
	public List<RTNode> traversePostOrder(RTNode root) 
	{
		if(root == null)
			throw new IllegalArgumentException("Node cannot be null.");
		
		List<RTNode> list = new ArrayList<RTNode>();
		list.add(root);
		
		if(! root.isLeaf())
		{
			for(int i = 0; i < root.usedSpace; i ++)
			{
				List<RTNode> a = traversePostOrder(((RTDirNode)root).getChild(i));
				for(int j = 0; j < a.size(); j ++)
				{
					list.add(a.get(j));
				}
			}
		}
		
		return list;
	}
	
	public static void main(String[] args) throws Exception 
	{
//		RTree tree = new RTree(3, 0.4f, Constants.RTREE_QUADRATIC, 2);
//		
//		float[] f = {10, 20, 40, 70,	//1
//			     30, 10, 70, 15,
//			     100, 70, 110, 80,		//3
//			     0, 50, 30, 55,
//			     13, 21, 54, 78,		//5
//			     3, 8, 23, 34,
//			     200, 29, 202, 50,
//			     34, 1, 35, 1,			//8
//			     201, 200, 234, 203,
//			     56, 69, 58, 70,		//10
//			     2, 67, 270, 102,
//			     1, 10, 310, 20,		//12
//			     23, 12, 345, 120,
//			     5, 34, 100, 340,
//			     19,100,450,560,	//15
//			     12,340,560,450,
//			     34,45,190,590,
//			     24,47,770,450,	//18
//			     
//			     91,99,390,980,
//			     89,10,99,100,	//20
//			     10,29,400,990,
//			     110,220,220,330,
//			     123,24,234,999	//23
//		};
//		
//		//插入结点
//		for(int i = 0; i < f.length;)
//		{
//			Point p1 = new Point(new float[]{f[i++],f[i++]});
//			Point p2 = new Point(new float[]{f[i++],f[i++]});
//			final Rectangle rectangle = new Rectangle(p1, p2);
//			tree.insert(rectangle);
//			
//			Rectangle[] rectangles = tree.root.datas;
//			System.out.println(tree.root.level);
//			for(int j = 0; j < rectangles.length; j ++)
//				System.out.println(rectangles[j]);
//		}
//		System.out.println("---------------------------------");
//		System.out.println("Insert finished.");
		
//		//删除结点
//		System.out.println("---------------------------------");
//		System.out.println("Begin delete.");
//		
//		for(int i = 0; i < f.length;)
//		{
//			Point p1 = new Point(new float[]{f[i++],f[i++]});
//			Point p2 = new Point(new float[]{f[i++],f[i++]});
//			final Rectangle rectangle = new Rectangle(p1, p2);
//			tree.delete(rectangle);
//			
//			Rectangle[] rectangles = tree.root.datas;
//			System.out.println(tree.root.level);
//			for(int j = 0; j < rectangles.length; j ++)
//				System.out.println(rectangles[j]);
//		}
//		
//		System.out.println("---------------------------------");
//		System.out.println("Delete finished.");
		
		
//		Rectangle[] rectangles = tree.root.datas;
//		for(int i = 0; i < rectangles.length; i ++)
//			System.out.println(rectangles[i]);
		
		
		System.out.println("---------------------------------");
		RTree tree = new RTree(5, 0.4f, Constants.RTREE_QUADRATIC,2);
		BufferedReader reader = new BufferedReader(new FileReader(new File("d:\\LB.txt")));
		String line ;
		while((line = reader.readLine()) != null)
		{
			String[] splits = line.split(" ");
			float lx = Float.parseFloat(splits[1]);
			float ly = Float.parseFloat(splits[2]);
			float hx = Float.parseFloat(splits[3]);
			float hy = Float.parseFloat(splits[4]);
			
			Point p1 = new Point(new float[]{lx,ly});
			Point p2 = new Point(new float[]{hx,hy});
			
			final Rectangle rectangle = new Rectangle(p1, p2);
			tree.insert(rectangle);
			
			Rectangle[] rectangles = tree.root.datas;
			System.out.println(tree.root.level);
			for(int j = 0; j < rectangles.length; j ++)
				System.out.println(rectangles[j]);
		}
		
		
		//删除结点
		System.out.println("---------------------------------");
		System.out.println("Begin delete.");
		
		reader = new BufferedReader(new FileReader(new File("d:\\LB.txt")));
		while((line = reader.readLine()) != null)
		{
			String[] splits = line.split(" ");
			float lx = Float.parseFloat(splits[1]);
			float ly = Float.parseFloat(splits[2]);
			float hx = Float.parseFloat(splits[3]);
			float hy = Float.parseFloat(splits[4]);
			
			Point p1 = new Point(new float[]{lx,ly});
			Point p2 = new Point(new float[]{hx,hy});
			
			final Rectangle rectangle = new Rectangle(p1, p2);
			tree.delete(rectangle);
			
			Rectangle[] rectangles = tree.root.datas;
			System.out.println(tree.root.level);
			for(int j = 0; j < rectangles.length; j ++)
				System.out.println(rectangles[j]);
		}
		
		System.out.println("---------------------------------");
		System.out.println("Delete finished.");
		
	}
 
}


package com.njupt.rtree;
 
import java.util.List;
 
import com.njupt.constants.Constants;
import com.njupt.spatial.INode;
 
/**
 * @ClassName RTNode 
 * @Description 
 */
public abstract class RTNode
{
	/**
	 * 结点所在的树
	 */
	protected RTree rtree;		
	
	/**
	 * 结点所在的层
	 */
	protected int level;		
	
	/**
	 * 相当于条目
	 */
	protected Rectangle[] datas;
	
//	/**
//	 * 结点的容量
//	 */
//	protected int capacity;
	
	/**
	 * 父节点
	 */
	protected RTNode parent;
	
	/**
	 * 结点已用的空间
	 */
	protected int usedSpace;
	
	/**
	 * 记录插入的搜索路径索引
	 */
	protected int insertIndex;
	
	/**
	 * 记录删除的查找路径索引
	 */
	protected int deleteIndex;
	
	public RTNode(RTree rtree, RTNode parent, int level) 
	{
		this.rtree = rtree;
		this.parent = parent;
		this.level = level;
//		this.capacity = capacity;
		datas = new Rectangle[rtree.getNodeCapacity() + 1];//多出的一个用于结点分裂
		usedSpace = 0;
	}
	
	/**
	 * @return 返回父节点
	 */
	public RTNode getParent()
	{
		return parent;
	}
	
//	/**
//	 * @return 结点容量
//	 */
//	public int getNodeCapacity()
//	{
//		return capacity;
//	}
	
	/**
	 * 向结点中添加Rectangle,即添加条目
	 * @param rectangle
	 */
	protected void addData(Rectangle rectangle) 
	{
		if(usedSpace == rtree.getNodeCapacity())
		{
			throw new IllegalArgumentException("Node is full.");
		}
		datas[usedSpace ++] = rectangle;
	}
 
	/**
	 * 删除结点中的第i个条目
	 * @param i
	 */
	protected void deleteData(int i)
	{
		if(datas[i + 1] != null)
		{
			System.arraycopy(datas, i + 1, datas, i, usedSpace - i -1);
			datas[usedSpace - 1] = null;
		}
		else
			datas[i] = null;
		usedSpace --;
	}
	
	/**
	 * 叶节点L中刚刚删除了一个条目,如果这个结点的条目数太少下溢,则删除该结点,同时将该结点中剩余的条目重定位到其他结点中。
	 * 如果有必要,要逐级向上进行这种删除,调整向上传递的路径上的所有外包矩形,使其尽可能小,直到根节点。
	 * @param list 存储删除结点中剩余条目
	 */
	protected void condenseTree(List<RTNode> list)
	{
		if(isRoot())
		{
			//根节点只有一个条目了,即只有左孩子或者右孩子
			if(! isLeaf() && usedSpace == 1)
			{
				RTDirNode root = (RTDirNode) this;
				
				RTNode child = root.getChild(0);
				root.children.remove(this);//gc
				child.parent = null;
//				if(child.level > 0)
//					child.level --;
				rtree.setRoot(child);
				
//				//所有结点的level减1,即树降低一层
//				List<RTNode> nodes = rtree.traversePostOrder(child);
//				for(int i = 0; i < nodes.size(); i ++)
//				{
//					nodes.get(i).level -= 1;
//				}
				
			}
		}else{
			RTNode parent = getParent();
			
			int min = Math.round(rtree.getNodeCapacity() * rtree.getFillFactor());
			if(usedSpace < min)
			{
				parent.deleteData(parent.deleteIndex);//其父节点中删除此条目
				((RTDirNode)parent).children.remove(this);
				this.parent = null;
				list.add(this);//之前已经把数据删除了
			}else{
				parent.datas[parent.deleteIndex] = getNodeRectangle();
			}
			parent.condenseTree(list);
		}
	}
	
	/**
	 * 分裂结点的平方算法<p>
	 * 1、为两个组选择第一个条目--调用算法pickSeeds()来为两个组选择第一个元素,分别把选中的两个条目分配到两个组当中。<br>
	 * 2、检查是否已经分配完毕,如果一个组中的条目太少,为避免下溢,将剩余的所有条目全部分配到这个组中,算法终止<br>
	 * 3、调用pickNext来选择下一个进行分配的条目--计算把每个条目加入每个组之后面积的增量,选择两个组面积增量差最大的条目索引,
	 * 	如果面积增量相等则选择面积较小的组,若面积也相等则选择条目数更少的组<br>
	 * @param rectangle 导致分裂的溢出Rectangle
	 * @return 两个组中的条目的索引
	 */
	protected int[][] quadraticSplit(Rectangle rectangle) 
	{
		if(rectangle == null)
		{
			throw new IllegalArgumentException("Rectangle cannot be null.");
		}
		
		datas[usedSpace] = rectangle;	//先添加进去
//		if(this instanceof RTDirNode)
//		{
//			(RTDirNode)(this).children.add()
//		}
		int total = usedSpace + 1;		//结点总数
		
		//标记访问的条目
		int[] mask = new int[total];
		for(int i = 0; i < total; i ++)
		{
			mask[i] = 1;
		}
		
		//每个组只是有total/2个条目
		int c = total/2 + 1;
		//每个结点最小条目个数
		int minNodeSize = Math.round(rtree.getNodeCapacity() * rtree.getFillFactor());
		//至少有两个
		if(minNodeSize < 2)
			minNodeSize = 2;
		
		//记录没有被检查的条目的个数
		int rem = total;
		
		int[] group1 = new int[c];//记录分配的条目的索引
		int[] group2 = new int[c];//记录分配的条目的索引
		//跟踪被插入每个组的条目的索引
		int i1 = 0, i2 = 0;
		
		int[] seed = pickSeeds();
		group1[i1 ++] = seed[0];
		group2[i2 ++] = seed[1];
		rem -=2;
		mask[group1[0]] = -1;
		mask[group2[0]] = -1;
		
		while(rem > 0)
		{
			//将剩余的所有条目全部分配到group1组中,算法终止
			if(minNodeSize - i1 == rem)
			{
				for(int i = 0; i < total; i ++)//总共rem个
				{
					if(mask[i] != -1)//还没有被分配
					{
						group1[i1 ++] = i;
						mask[i] = -1;
						rem --;
					}
				}
			//将剩余的所有条目全部分配到group1组中,算法终止
			}else if(minNodeSize - i2 == rem)
			{
				for(int i = 0; i < total; i ++)//总共rem个
				{
					if(mask[i] != -1)//还没有被分配
					{
						group2[i2 ++] = i;
						mask[i] = -1;
						rem --;
					}
				}
			}else
			{
				//求group1中所有条目的最小外包矩形
				Rectangle mbr1 = (Rectangle) datas[group1[0]].clone();
				for(int i = 1; i < i1; i ++)
				{
					mbr1 = mbr1.getUnionRectangle(datas[group1[i]]);
				}
				//求group2中所有条目的外包矩形
				Rectangle mbr2 = (Rectangle) datas[group2[0]].clone();
				for(int i = 1; i < i2; i ++)
				{
					mbr2 = mbr2.getUnionRectangle(datas[group2[i]]);
				}
				
				//找出下一个进行分配的条目
				double dif = Double.NEGATIVE_INFINITY;
				double areaDiff1 = 0, areaDiff2 = 0;
				int sel = -1;				
				for(int i = 0; i < total; i ++)
				{
					if(mask[i] != -1)//还没有被分配的条目
					{
						//计算把每个条目加入每个组之后面积的增量,选择两个组面积增量差最大的条目索引
						Rectangle a = mbr1.getUnionRectangle(datas[i]);
						areaDiff1 = a.getArea() - mbr1.getArea();
						
						Rectangle b = mbr2.getUnionRectangle(datas[i]);
						areaDiff2 = b.getArea() - mbr2.getArea();
						
						if(Math.abs(areaDiff1 - areaDiff2) > dif)
						{
							dif = Math.abs(areaDiff1 - areaDiff2);
							sel = i;
						}
					}
				}
				
				if(areaDiff1 < areaDiff2)//先比较面积增量
				{
					group1[i1 ++] = sel;
				}else if(areaDiff1 > areaDiff2)
				{
					group2[i2 ++] = sel;
				}else if(mbr1.getArea() < mbr2.getArea())//再比较自身面积
				{
					group1[i1 ++] = sel;
				}else if(mbr1.getArea() > mbr2.getArea())
				{
					group2[i2 ++] = sel;
				}else if(i1 < i2)//最后比较条目个数
				{
					group1[i1 ++] = sel;
				}else if(i1 > i2)
				{
					group2[i2 ++] = sel;
				}else {
					group1[i1 ++] = sel;
				}
				mask[sel] = -1;
				rem --;
				
			}
		}//end while
		
		
		int[][] ret = new int[2][];
		ret[0] = new int[i1];
		ret[1] = new int[i2];
		
		for(int i = 0; i < i1; i ++)
		{
			ret[0][i] = group1[i];
		}
		for(int i = 0; i < i2; i ++)
		{
			ret[1][i] = group2[i];
		}
		return ret;
	}
	
	
	/**
	 * 1、对每一对条目E1和E2,计算包围它们的Rectangle J,计算d = area(J) - area(E1) - area(E2);<br>
	 * 2、Choose the pair with the largest d
	 * @return 返回两个条目如果放在一起会有最多的冗余空间的条目索引
	 */
	protected int[] pickSeeds() 
	{
		double inefficiency = Double.NEGATIVE_INFINITY;
		int i1 = 0, i2 = 0;
		
		//
		for(int i = 0; i < usedSpace; i ++)
		{
			for(int j = i + 1; j <= usedSpace; j ++)//注意此处的j值
			{
				Rectangle rectangle = datas[i].getUnionRectangle(datas[j]);
				double d = rectangle.getArea() - datas[i].getArea() - datas[j].getArea();
				
				if(d > inefficiency)
				{
					inefficiency = d;
					i1 = i;
					i2 = j;
				}
			}
		}
		return new int[]{i1, i2};
	}
 
	/**
	 * @return 返回包含结点中所有条目的最小Rectangle
	 */
	public Rectangle getNodeRectangle()
	{
		if(usedSpace > 0)
		{
			Rectangle[] rectangles = new Rectangle[usedSpace];
			System.arraycopy(datas, 0, rectangles, 0, usedSpace);
			return Rectangle.getUnionRectangle(rectangles);
		}else {
			return new Rectangle(new Point(new float[]{0,0}), new Point(new float[]{0,0}));
		}
	}
	
	/**
	 * @return 是否根节点
	 */
	public boolean isRoot()
	{
		return (parent == Constants.NULL);
	}
	
	/**
	 * @return 是否非叶子结点
	 */
	public boolean isIndex()
	{
		return (level != 0);
	}
	
	/**
	 * @return 是否叶子结点
	 */
	public boolean isLeaf()
	{
		return (level == 0);
	}
 
	
	/**
	 * <b>步骤CL1:</b>初始化——记R树的根节点为N。<br>
	 * <b>步骤CL2:</b>检查叶节点——如果N是个叶节点,返回N<br>
	 * <b>步骤CL3:</b>选择子树——如果N不是叶节点,则从N中所有的条目中选出一个最佳的条目F,
	 *		选择的标准是:如果E加入F后,F的外廓矩形FI扩张最小,则F就是最佳的条目。如果有两个
	 *		条目在加入E后外廓矩形的扩张程度相等,则在这两者中选择外廓矩形较小的那个。<br>
	 * <b>步骤CL4:</b>向下寻找直至达到叶节点——记Fp指向的孩子节点为N,然后返回步骤CL2循环运算,
	 * 		直至查找到叶节点。<p>
	 * @param Rectangle 
	 * @return RTDataNode
	 */
	protected abstract RTDataNode chooseLeaf(Rectangle rectangle);
	
	/**
	 * R树的根节点为T,查找包含rectangle的叶子结点<p>
	 * 1、如果T不是叶子结点,则逐个查找T中的每个条目是否包围rectangle,若包围则递归调用findLeaf()<br>
	 * 2、如果T是一个叶子结点,则逐个检查T中的每个条目能否匹配rectangle<br>
	 * @param rectangle
	 * @return 返回包含rectangle的叶节点
	 */
	protected abstract RTDataNode findLeaf(Rectangle rectangle);
	
}


package com.njupt.rtree;
 
import java.util.ArrayList;
import java.util.List;
 
import com.njupt.constants.Constants;
 
/**
 * @ClassName RTDataNode 
 * @Description 
 */
public class RTDataNode extends RTNode
{
 
	public RTDataNode(RTree rTree, RTNode parent) 
	{
		super(rTree, parent, 0);
	}
	
	/**
	 * 在叶节点中插入Rectangle,插入后如果其父节点不为空则需要向上调整树直到根节点;<br>
	 * 若插入Rectangle之后超过结点容量则需要分裂结点
	 * @param rectangle
	 * @return
	 */
	public boolean insert(Rectangle rectangle) 
	{
		if(usedSpace < rtree.getNodeCapacity())
		{
			datas[usedSpace ++] = rectangle;
			RTDirNode parent = (RTDirNode) getParent();
			
			if(parent != null)
				parent.adjustTree(this, null);
			return true;
			
		}else{//超过结点容量
			RTDataNode[] splitNodes = splitLeaf(rectangle);
			RTDataNode l = splitNodes[0];
			RTDataNode ll = splitNodes[1];
			
			if(isRoot())
			{
				//根节点已满,需要分裂。创建新的根节点
				RTDirNode rDirNode = new RTDirNode(rtree, Constants.NULL, level + 1);
				rtree.setRoot(rDirNode);
				rDirNode.addData(l.getNodeRectangle());
				rDirNode.addData(ll.getNodeRectangle());
				
				ll.parent = rDirNode;
				l.parent = rDirNode;
				
				rDirNode.children.add(l);
				rDirNode.children.add(ll);
				
			}else{//不是根节点
				RTDirNode parentNode = (RTDirNode) getParent();
				parentNode.adjustTree(l, ll);
			}
			
			
		}
		return true;
	}
 
	/**
	 * 插入Rectangle之后超过容量需要分裂
	 * @param rectangle
	 * @return
	 */
	public RTDataNode[] splitLeaf(Rectangle rectangle) 
	{
		int[][] group = null;
		
		switch(rtree.getTreeType())
		{
			case Constants.RTREE_LINEAR:
				break;
			case Constants.RTREE_QUADRATIC:
				group = quadraticSplit(rectangle);
				break;
			case Constants.RTREE_EXPONENTIAL:
				break;
			case Constants.RSTAR:
				break;
			default:
				throw new IllegalArgumentException("Invalid tree type.");
		}
		
		RTDataNode l = new RTDataNode(rtree, parent);
		RTDataNode ll = new RTDataNode(rtree, parent);
		
		int[] group1 = group[0];
		int[] group2 = group[1];
		
		for(int i = 0; i < group1.length; i ++)
		{
			l.addData(datas[group1[i]]);
		}
		
		for(int i = 0; i < group2.length; i ++)
		{
			ll.addData(datas[group2[i]]);
		}
		return new RTDataNode[]{l, ll};
	}
 
 
	@Override
	public RTDataNode chooseLeaf(Rectangle rectangle) 
	{
		insertIndex = usedSpace;//记录插入路径的索引
		return this;
	}
 
	/**
	 * 从叶节点中删除此条目rectangle<p>
	 * 先删除此rectangle,再调用condenseTree()返回删除结点的集合,把其中的叶子结点中的每个条目重新插入;
	 * 非叶子结点就从此结点开始遍历所有结点,然后把所有的叶子结点中的所有条目全部重新插入
	 * @param rectangle
	 * @return
	 */
	protected int delete(Rectangle rectangle) 
	{
		for(int i = 0; i < usedSpace; i ++)
		{
			if(datas[i].equals(rectangle))
			{
				deleteData(i);
				List<RTNode> deleteEntriesList = new ArrayList<RTNode>();
				condenseTree(deleteEntriesList);
				
				//重新插入删除结点中剩余的条目
				for(int j = 0; j < deleteEntriesList.size(); j ++)
				{
					RTNode node = deleteEntriesList.get(j);
					if(node.isLeaf())//叶子结点,直接把其上的数据重新插入
					{
						for(int k = 0; k < node.usedSpace; k ++)
						{
							rtree.insert(node.datas[k]);
						}
					}else{//非叶子结点,需要先后序遍历出其上的所有结点
						List<RTNode> traverseNodes = rtree.traversePostOrder(node);
						
						//把其中的叶子结点中的条目重新插入
						for(int index = 0; index < traverseNodes.size(); index ++)
						{
							RTNode traverseNode = traverseNodes.get(index);
							if(traverseNode.isLeaf())
							{
								for(int t = 0; t < traverseNode.usedSpace; t ++)
								{
									rtree.insert(traverseNode.datas[t]);
								}
							}
						}
						
					}
				}
				
				return deleteIndex;
			}//end if
		}//end for
		return -1;
	}
 
	@Override
	protected RTDataNode findLeaf(Rectangle rectangle) 
	{
		for(int i = 0; i < usedSpace; i ++)
		{
			if(datas[i].enclosure(rectangle))
			{
				deleteIndex = i;//记录搜索路径
				return this;
			}
		}
		return null;
	}
 
}


package com.njupt.rtree;
 
import java.util.ArrayList;
import java.util.List;
 
import com.njupt.constants.Constants;
 
/**
 * @ClassName RTDirNode 
 * @Description 非叶节点
 */
public class RTDirNode extends RTNode
{
	/**
	 * 孩子结点
	 */
	protected List<RTNode> children;
	
	public RTDirNode(RTree rtree, RTNode parent, int level) 
	{
		super(rtree, parent, level);
		children = new ArrayList<RTNode>();
	}
	
	/**
	 * @param index
	 * @return 对应索引下的孩子结点
	 */
	public RTNode getChild(int index)
	{
		return children.get(index);
	}
 
	@Override
	public RTDataNode chooseLeaf(Rectangle rectangle) 
	{
		int index;
		
		switch (rtree.getTreeType()) 
		{
			case Constants.RTREE_LINEAR:
				
			case Constants.RTREE_QUADRATIC:
				
			case Constants.RTREE_EXPONENTIAL:
				index = findLeastEnlargement(rectangle);
				break;
			case Constants.RSTAR:
				if(level == 1)//即此结点指向叶节点
				{
					index = findLeastOverlap(rectangle);
				}else{
					index = findLeastEnlargement(rectangle);
				}
				break;
				
			default:
				throw new IllegalStateException("Invalid tree type.");
		}
		
		insertIndex = index;//记录插入路径的索引
		
		return getChild(index).chooseLeaf(rectangle);
	}
 
	/**
	 * @param rectangle
	 * @return 返回最小重叠面积的结点的索引,如果重叠面积相等则选择加入此Rectangle后面积增量更小的,如果面积增量还相等则选择自身面积更小的
	 */
	private int findLeastOverlap(Rectangle rectangle) 
	{
		float overlap = Float.POSITIVE_INFINITY;
		int sel = -1;
		
		for(int i = 0; i < usedSpace; i ++)
		{
			RTNode node = getChild(i);
			float ol = 0;
			
			for(int j = 0; j < node.datas.length; j ++)
			{
				ol += rectangle.intersectingArea(node.datas[j]);
			}
			if(ol < overlap)
			{
				overlap = ol;//记录重叠面积最小的
				sel = i;//记录第几个孩子的索引
			}else if(ol == overlap)//如果重叠面积相等则选择加入此Rectangle后面积增量更小的,如果面积增量还相等则选择自身面积更小的
			{
				double area1 = datas[i].getUnionRectangle(rectangle).getArea() - datas[i].getArea();
				double area2 = datas[sel].getUnionRectangle(rectangle).getArea() - datas[sel].getArea();
				
				if(area1 == area2)
				{
					sel = (datas[sel].getArea() <= datas[i].getArea()) ? sel : i;
				}else{
					sel = (area1 < area2) ? i : sel;
				}
			}
		}
		return sel;
	}
 
	/**
	 * @param rectangle
	 * @return 面积增量最小的结点的索引,如果面积增量相等则选择自身面积更小的
	 */
	private int findLeastEnlargement(Rectangle rectangle) 
	{
		double area = Double.POSITIVE_INFINITY;
		int sel = -1;
		
		for(int i = 0; i < usedSpace; i ++)
		{
			double enlargement = datas[i].getUnionRectangle(rectangle).getArea() - datas[i].getArea();
			if(enlargement < area)
			{
				area = enlargement;
				sel = i;
			}else if(enlargement == area)
			{
				sel = (datas[sel].getArea() < datas[i].getArea()) ? sel : i;
			}
		}
		
		return sel;
	}
 
	/**
	 * 插入新的Rectangle后从插入的叶节点开始向上调整RTree,直到根节点
	 * @param node1 引起需要调整的孩子结点
	 * @param node2  分裂的结点,若未分裂则为null
	 */
	public void adjustTree(RTNode node1, RTNode node2) 
	{
		//先要找到指向原来旧的结点(即未添加Rectangle之前)的条目的索引
		datas[insertIndex] = node1.getNodeRectangle();//先用node1覆盖原来的结点
		children.set(insertIndex, node1);//替换旧的结点
		
		if(node2 != null)
		{
			insert(node2);//插入新的结点
			
		}else if(! isRoot())//还没到达根节点
		{
			RTDirNode parent = (RTDirNode) getParent();
			parent.adjustTree(this, null);//向上调整直到根节点
		}
	}
 
	/**
	 * @param node
	 * @return 如果结点需要分裂则返回true
	 */
	protected boolean insert(RTNode node) 
	{
		if(usedSpace < rtree.getNodeCapacity())
		{
			datas[usedSpace ++] = node.getNodeRectangle();
			children.add(node);//新加的
			node.parent = this;//新加的
			RTDirNode parent = (RTDirNode) getParent();
			if(parent != null)
			{
				parent.adjustTree(this, null);
			}
			return false;
		}else{//非叶子结点需要分裂
			RTDirNode[] a = splitIndex(node);
			RTDirNode n = a[0];
			RTDirNode nn = a[1];
			
			if(isRoot())
			{
				//新建根节点,层数加1
				RTDirNode newRoot = new RTDirNode(rtree, Constants.NULL, level + 1);
				
				//还需要把原来结点的孩子添加到两个分裂的结点n和nn中,此时n和nn的孩子结点还为空
//				for(int i = 0; i < n.usedSpace; i ++)
//				{
//					n.children.add(this.children.get(index));
//				}
//				
//				for(int i = 0; i < nn.usedSpace; i ++)
//				{
//					nn.children.add(this.children.get(index));
//				}
				
				//把两个分裂的结点n和nn添加到根节点
				newRoot.addData(n.getNodeRectangle());
				newRoot.addData(nn.getNodeRectangle());
				
				newRoot.children.add(n);
				newRoot.children.add(nn);
				
				//设置两个分裂的结点n和nn的父节点
				n.parent = newRoot;
				nn.parent = newRoot;
				
				//最后设置rtree的根节点
				rtree.setRoot(newRoot);//新加的
			}else {
				RTDirNode p = (RTDirNode) getParent();
				p.adjustTree(n, nn);
			}
		}
		return true;
	}
 
	/**
	 * 非叶子结点的分裂
	 * 
	 * @param node
	 * @return
	 */
	private RTDirNode[] splitIndex(RTNode node) 
	{
		int[][] group = null;
		
		switch (rtree.getTreeType()) 
		{
			case Constants.RTREE_LINEAR:
				break;
			case Constants.RTREE_QUADRATIC:
				group = quadraticSplit(node.getNodeRectangle());
				children.add(node);//新加的
				node.parent = this;//新加的
				break;
			case Constants.RTREE_EXPONENTIAL:
				break;
			case Constants.RSTAR:
				break;
			default:
				throw new IllegalStateException("Invalid tree type.");
		}
		
		RTDirNode index1 = new RTDirNode(rtree, parent, level);
		RTDirNode index2 = new RTDirNode(rtree, parent, level);
		
		int[] group1 = group[0];
		int[] group2 = group[1];
		
		for(int i = 0; i < group1.length; i ++)
		{
			//为index1添加数据和孩子
			index1.addData(datas[group1[i]]);
			index1.children.add(this.children.get(group1[i]));//新加的
			//让index1成为其父节点
			this.children.get(group1[i]).parent = index1;//新加的
		}
		for(int i = 0; i < group2.length; i ++)
		{
			index2.addData(datas[group2[i]]);
			index2.children.add(this.children.get(group2[i]));//新加的
			this.children.get(group2[i]).parent = index2;//新加的
		}
		
		return new RTDirNode[]{index1,index2};
	}
 
	@Override
	protected RTDataNode findLeaf(Rectangle rectangle) 
	{
		for(int i = 0; i < usedSpace; i ++)
		{
			if(datas[i].enclosure(rectangle))
			{
				deleteIndex = i;//记录搜索路径
				RTDataNode leaf = children.get(i).findLeaf(rectangle);
				if(leaf != null)
					return leaf;
			}
		}
		return null;
	}
 
}


package com.njupt.rtree;
 
/**
 * 外包矩形
 * @ClassName Rectangle 
 * @Description 
 */
public class Rectangle implements Cloneable
{
	private Point low;
	private Point high;
	
	public Rectangle(Point p1, Point p2)
	{
		if(p1 == null || p2 == null)
		{
			throw new IllegalArgumentException("Points cannot be null.");
		}
		if(p1.getDimension() != p2.getDimension())
		{
			throw new IllegalArgumentException("Points must be of same dimension.");
		}
		//先左下角后右上角
		for(int i = 0; i < p1.getDimension(); i ++)
		{
			if(p1.getFloatCoordinate(i) > p2.getFloatCoordinate(i))
			{
				throw new IllegalArgumentException("坐标点为先左下角后右上角");
			}
		}
		low = (Point) p1.clone();
		high = (Point) p2.clone();
	}
	
	/**
	 * 返回Rectangle左下角的Point
	 * @return Point
	 */
	public Point getLow() 
	{
		return (Point) low.clone();
	}
 
	/**
	 * 返回Rectangle右上角的Point
	 * @return Point
	 */
	public Point getHigh() 
	{
		return high;
	}
 
	/**
	 * @param rectangle
	 * @return 包围两个Rectangle的最小Rectangle
	 */
	public Rectangle getUnionRectangle(Rectangle rectangle)
	{
		if(rectangle == null)
			throw new IllegalArgumentException("Rectangle cannot be null.");
		
		if(rectangle.getDimension() != getDimension())
		{
			throw new IllegalArgumentException("Rectangle must be of same dimension.");
		}
		
		float[] min = new float[getDimension()];
		float[] max = new float[getDimension()];
		
		for(int i = 0; i < getDimension(); i ++)
		{
			min[i] = Math.min(low.getFloatCoordinate(i), rectangle.low.getFloatCoordinate(i));
			max[i] = Math.max(high.getFloatCoordinate(i), rectangle.high.getFloatCoordinate(i));
		}
		
		return new Rectangle(new Point(min), new Point(max));
	}
	
	/**
	 * @return 返回Rectangle的面积
	 */
	public float getArea()
	{
		float area = 1;
		for(int i = 0; i < getDimension(); i ++)
		{
			area *= high.getFloatCoordinate(i) - low.getFloatCoordinate(i);
		}
		
		return area;
	}
	
	/**
	 * @param rectangles
	 * @return 包围一系列Rectangle的最小Rectangle
	 */
	public static Rectangle getUnionRectangle(Rectangle[] rectangles)
	{
		if(rectangles == null || rectangles.length == 0)
			throw new IllegalArgumentException("Rectangle array is empty.");
		
		Rectangle r0 = (Rectangle) rectangles[0].clone();
		for(int i = 1; i < rectangles.length; i ++)
		{
			r0 = r0.getUnionRectangle(rectangles[i]);
		}
		
		return r0;
	}
	
	@Override
	protected Object clone()
	{
		Point p1 = (Point) low.clone();
		Point p2 = (Point) high.clone();
		return new Rectangle(p1, p2);
	}
	
	@Override
	public String toString() 
	{
		return "Rectangle Low:" + low + " High:" + high;
	}
	
	public static void main(String[] args) 
	{
		float[] f1 = {1.3f,2.4f};
		float[] f2 = {3.4f,4.5f};
		Point p1 = new Point(f1);
		Point p2 = new Point(f2);
		Rectangle rectangle = new Rectangle(p1, p2);
		System.out.println(rectangle);
//		Point point = rectangle.getHigh();
//		point = p1;
//		System.out.println(rectangle);
		
		float[] f_1 = {0f,0f};
		float[] f_2 = {-2f,2f};
		float[] f_3 = {3f,3f};
		float[] f_4 = {2.5f,2.5f};
		float[] f_5 = {1.5f,1.5f};
		p1 = new Point(f_1);
		p2 = new Point(f_2);
		Point p3 = new Point(f_3);
		Point p4 = new Point(f_4);
		Point p5 = new Point(f_5);
		Rectangle re1 = new Rectangle(p1, p2);
		Rectangle re2 = new Rectangle(p2, p3);
		Rectangle re3 = new Rectangle(p4, p3);
//		Rectangle re4 = new Rectangle(p3, p4);
		Rectangle re5 = new Rectangle(p5, p4);
		System.out.println(re1.isIntersection(re2));
		System.out.println(re1.isIntersection(re3));
		System.out.println(re1.intersectingArea(re2));
		System.out.println(re1.intersectingArea(re5));
	}
 
	/**
	 * 两个Rectangle相交的面积
	 * @param rectangle Rectangle
	 * @return float
	 */
	public float intersectingArea(Rectangle rectangle) 
	{
		if(! isIntersection(rectangle))
		{
			return 0;
		}
		
		float ret = 1;
		for(int i = 0; i < rectangle.getDimension(); i ++)
		{
			float l1 = this.low.getFloatCoordinate(i);
			float h1 = this.high.getFloatCoordinate(i);
			float l2 = rectangle.low.getFloatCoordinate(i);
			float h2 = rectangle.high.getFloatCoordinate(i);
			
			//rectangle1在rectangle2的左边
			if(l1 <= l2 && h1 <= h2)
			{
				ret *= (h1 - l1) - (l2 - l1);
			}else if(l1 >= l2 && h1 >= h2)
			//rectangle1在rectangle2的右边
			{
				ret *= (h2 - l2) - (l1 - l2);
			}else if(l1 >= l2 && h1 <= h2)			
			//rectangle1在rectangle2里面
			{
				ret *= h1 - l1;
			}else if(l1 <= l2 && h1 >= h2)	
			//rectangle1包含rectangle2
			{
				ret *= h2 - l2;
			}
		}
		return ret;
	}
	
	/**
	 * @param rectangle
	 * @return 判断两个Rectangle是否相交
	 */
	public boolean isIntersection(Rectangle rectangle)
	{
		if(rectangle == null)
			throw new IllegalArgumentException("Rectangle cannot be null.");
		
		if(rectangle.getDimension() != getDimension())
		{
			throw new IllegalArgumentException("Rectangle cannot be null.");
		}
		
		
		for(int i = 0; i < getDimension(); i ++)
		{
			if(low.getFloatCoordinate(i) > rectangle.high.getFloatCoordinate(i) ||
					high.getFloatCoordinate(i) < rectangle.low.getFloatCoordinate(i))
			{
				return false;
			}
		}
		return true;
	}
 
	/**
	 * @return 返回Rectangle的维度
	 */
	private int getDimension() 
	{
		return low.getDimension();
	}
 
	/**
	 * 判断rectangle是否被包围
	 * @param rectangle
	 * @return
	 */
	public boolean enclosure(Rectangle rectangle) 
	{
		if(rectangle == null)
			throw new IllegalArgumentException("Rectangle cannot be null.");
		
		if(rectangle.getDimension() != getDimension())
			throw new IllegalArgumentException("Rectangle dimension is different from current dimension.");
		
		for(int i = 0; i < getDimension(); i ++)
		{
			if(rectangle.low.getFloatCoordinate(i) < low.getFloatCoordinate(i) ||
					rectangle.high.getFloatCoordinate(i) > high.getFloatCoordinate(i))
				return false;
		}
		return true;
	}
	
	@Override
	public boolean equals(Object obj) 
	{
		if(obj instanceof Rectangle)
		{
			Rectangle rectangle = (Rectangle) obj;
			if(low.equals(rectangle.getLow()) && high.equals(rectangle.getHigh()))
				return true;
		}
		return false;
	}
}


package com.njupt.constants;
 
import com.njupt.rtree.RTNode;
 
public class Constants 
{
	public static final int MAX_NUMBER_OF_ENTRIES_IN_NODE = 20;//结点中的最大条目数
	public static final int MIN_NUMBER_OF_ENTRIES_IN_NODE = 8;//结点中的最小条目数
	
	public static final int RTDataNode_Dimension = 2;
	
	/** Available RTree variants. */
    public static final int RTREE_LINEAR = 0;
    public static final int RTREE_QUADRATIC = 1;
    public static final int RTREE_EXPONENTIAL = 2;
    public static final int RSTAR = 3;
    
    public static final int NIL = -1;
	public static final RTNode NULL = null;
}


package com.njupt.rtree;
 
/**
 * @ClassName Point 
 * @Description n维空间中的点,所有的维度被存储在一个float数组中
 */
public class Point implements Cloneable
{
	private float[] data;
	
	public Point(float[] data)
	{
		if(data == null)
		{
			throw new IllegalArgumentException("Coordinates cannot be null.");
		}
		if(data.length < 2)
		{
			throw new IllegalArgumentException("Point dimension should be greater than 1.");
		}
		
		this.data = new float[data.length];
		System.arraycopy(data, 0, this.data, 0, data.length);
	}
	
	public Point(int[] data)
	{
		if(data == null)
		{
			throw new IllegalArgumentException("Coordinates cannot be null.");
		}
		if(data.length < 2)
		{
			throw new IllegalArgumentException("Point dimension should be greater than 1.");
		}
		
		this.data = new float[data.length];
		for(int i = 0 ; i < data.length ; i ++)
		{
			this.data[i] = data[i];
		}
	}
	
	@Override
	protected Object clone()
	{
		float[] copy = new float[data.length];
		System.arraycopy(data, 0, copy, 0, data.length);
		return new Point(copy);
	}
	
	@Override
	public String toString() 
	{
		StringBuffer sBuffer = new StringBuffer("(");
		
		for(int i = 0 ; i < data.length - 1 ; i ++)
		{
			sBuffer.append(data[i]).append(",");
		}
		
		sBuffer.append(data[data.length - 1]).append(")");
		
		return sBuffer.toString();
	}
	
	public static void main(String[] args) 
	{
		float[] test = {1.2f,2f,34f};
		Point point1 = new Point(test);
		System.out.println(point1);
		
		int[] test2 = {1,2,3,4};
		point1 = new Point(test2);
		System.out.println(point1);
	}
 
	/**
	 * @return 返回Point的维度
	 */
	public int getDimension() 
	{
		return data.length;
	}
 
	/**
	 * @param index
	 * @return 返回Point坐标第i位的float值
	 */
	public float getFloatCoordinate(int index) 
	{
		return data[index];
	}
	
	/**
	 * @param index
	 * @return 返回Point坐标第i位的int值
	 */
	public int getIntCoordinate(int index)
	{
		return (int) data[index];
	}
	
	@Override
	public boolean equals(Object obj) 
	{
		if(obj instanceof Point)
		{
			Point point = (Point) obj;
			
			if(point.getDimension() != getDimension())
				throw new IllegalArgumentException("Points must be of equal dimensions to be compared.");
			
			for(int i = 0; i < getDimension(); i ++)
			{
				if(getFloatCoordinate(i) != point.getFloatCoordinate(i))
					return false;
			}
		}
		
		if(! (obj instanceof Point))
			return false;
		
		return true;
	}
}

原博客请看链接

> https://blog.csdn.net/renyisheng/article/details/40347223


  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
可以使用以下步骤来实现JSON形结构: 1. 解析JSON字符串,将其转换为JSON对象。 2. 创建根节点。 3. 遍历JSON对象,将每个键值对转换为一个节点,并将其添加到根节点下。 4. 对于每个节点,如果对应的值是一个对象或数组,则递归地调用步骤3,将其添加为该节点的子节点。 5. 将根节点作为的根,返回根节点。 以下是一个示例代码: ```java import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; public class JsonTree { public static TreeNode buildTree(String jsonString) { JSONObject json = JSONObject.parseObject(jsonString); TreeNode root = new TreeNode("root"); buildSubTree(root, json); return root; } private static void buildSubTree(TreeNode parentNode, Object obj) { if (obj instanceof JSONObject) { JSONObject jsonObject = (JSONObject) obj; for (String key : jsonObject.keySet()) { TreeNode node = new TreeNode(key); parentNode.addChild(node); buildSubTree(node, jsonObject.get(key)); } } else if (obj instanceof JSONArray) { JSONArray jsonArray = (JSONArray) obj; for (int i = 0; i < jsonArray.size(); i++) { TreeNode node = new TreeNode("[" + i + "]"); parentNode.addChild(node); buildSubTree(node, jsonArray.get(i)); } } else { String value = obj.toString(); TreeNode node = new TreeNode(value); parentNode.addChild(node); } } public static void main(String[] args) { String jsonString = "{\"name\":\"John\",\"age\":30,\"city\":\"New York\",\"cars\":[{\"name\":\"Ford\",\"models\":[\"Fiesta\",\"Focus\",\"Mustang\"]},{\"name\":\"BMW\",\"models\":[\"320\",\"X3\",\"X5\"]},{\"name\":\"Fiat\",\"models\":[\"500\",\"Panda\"]}]}"; TreeNode root = buildTree(jsonString); root.print(); } } class TreeNode { private String name; private List<TreeNode> children; public TreeNode(String name) { this.name = name; this.children = new ArrayList<>(); } public void addChild(TreeNode child) { children.add(child); } public void print() { print(0); } private void print(int level) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < level; i++) { sb.append(" "); } sb.append(name); System.out.println(sb.toString()); for (TreeNode child : children) { child.print(level + 1); } } } ``` 这里使用了阿里巴巴的 fastjson 库来解析 JSON 字符串,并且将节点用一个 TreeNode 类来表示,该类包含节点名称和子节点列表。在 buildTree 方法中,我们首先将 JSON 字符串解析为 JSONObject,然后创建根节点,最后通过递归调用 buildSubTree 方法来构建子。在 buildSubTree 方法中,我们判断当前节点对应的值类型,如果是 JSONObject 或 JSONArray,则递归构建其子节点;否则,直接将其作为叶子节点添加到当前节点下。最后调用根节点的 print 方法来输出形结构。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值