求大楼整体轮廓线的数组

问题描述:

给定一个 N×3 的矩阵 matrix,对于每一个长度为 3 的小数组 arr,都表示一个大楼的三个数 据。arr[0]表示大楼的左边界,arr[1]表示大楼的右边界,arr[2]表示大楼的高度(一定大于 0)。 每座大楼的地基都在 X 轴上,大楼之间可能会有重叠,请返回整体的轮廓线数组。

样例:

输入:

matrix = { {2,5,6}, {1,7,4}, {4,6,7}, {3,6,5}, {10,13,2}, {9,11,3}, {12,14,4}, {10,12,5} }

 输出:

{{1,2,4}, {2,4,6}, {4,6,7}, {6,7,4}, {9,10,3}, {10,12,5}, {12,14,4}}

 求大楼整体轮廓线的数组

求解思路:

  1. 首先将每个输入的数组转换成两个数组,一个表示左端点上升了多大高度,一个表示右端点下降了多少高度。用true表示上升,false表示下降,如{2,5,6}可以改写成{2,true,6}{5,false,6}
  2. 将输入的数组转换完毕后,按照规定的比较规则进行排序,比较规则为:
    • 按照x坐标的大小排序,x坐标小的排在x坐标大的前面
    • 如果x坐标相等,看该数组是上升还是下降,上升在前下降在后
  3. 准备两个有序表,这里采用TreeMap代替(底层为红黑树),一个(map1)存储高度和对应的数量,一个(map2)存储x坐标和对应的最大高度
  4. 开始遍历处理完后的数组
    • 如果当前数组对于加入操作,则先查看map1中是否有这个高度,有则对应数量+1,否则创建记录、
    • 更新map2中的记录,如果map2中没记录当前x对应的高度,则创建新纪录;如果创建过当前x的高度,则更新其高度。高度为map1中的最后一个记录。
  5. 根据map2生成对应大楼外轮廓图

Coding

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;

public class Problem01_BuildingOutline {

	// 描述高度变化的对象
	public static class Node {
		public int x; // x轴上的值
		public boolean isAdd;// true为加入,false为删除
		public int h; // 高度

		public Node(int x, boolean isAdd, int h) {
			this.x = x;
			this.isAdd = isAdd;
			this.h = h;
		}
	}

	// 排序的比较策略
	// 1,第一个维度的x值从小到大。
	// 2,如果第一个维度的值相等,看第二个维度的值,“加入”排在前,“删除”排在后
	// 3,如果两个对象第一维度和第二个维度的值都相等,则认为两个对象相等,谁在前都行。
	public static class NodeComparator implements Comparator<Node> {
		@Override
		public int compare(Node o1, Node o2) {
			if (o1.x != o2.x) {
				return o1.x - o2.x;
			}
			if (o1.isAdd != o2.isAdd) {
				return o1.isAdd ? -1 : 1;
			}
			return 0;
		}
	}

	// 全部流程的主方法
	public static List<List<Integer>> buildingOutline(int[][] matrix) {
		Node[] nodes = new Node[matrix.length * 2];
		// 每一个大楼轮廓数组,产生两个描述高度变化的对象
		for (int i = 0; i < matrix.length; i++) {
			nodes[i * 2] = new Node(matrix[i][0], true, matrix[i][2]);
			nodes[i * 2 + 1] = new Node(matrix[i][1], false, matrix[i][2]);
		}

		// 把描述高度变化的对象数组,按照规定的排序策略排序
		Arrays.sort(nodes, new NodeComparator());

		// TreeMap就是java中的红黑树结构,直接当作有序表来使用
		TreeMap<Integer, Integer> mapHeightTimes = new TreeMap<>();
		TreeMap<Integer, Integer> mapXvalueHeight = new TreeMap<>();
		for (int i = 0; i < nodes.length; i++) {
			if (nodes[i].isAdd) { // 如果当前是加入操作
				if (!mapHeightTimes.containsKey(nodes[i].h)) { // 没有出现的高度直接新加记录
					mapHeightTimes.put(nodes[i].h, 1);
				} else { // 之前出现的高度,次数加1即可
					mapHeightTimes.put(nodes[i].h,
							mapHeightTimes.get(nodes[i].h) + 1);
				}
			} else { // 如果当前是删除操作
				if (mapHeightTimes.get(nodes[i].h) == 1) { // 如果当前的高度出现次数为1,直接删除记录
					mapHeightTimes.remove(nodes[i].h);
				} else { // 如果当前的高度出现次数大于1,次数减1即可
					mapHeightTimes.put(nodes[i].h,
							mapHeightTimes.get(nodes[i].h) - 1);
				}
			}

			// 根据mapHeightTimes中的最大高度,设置mapXvalueHeight表
			if (mapHeightTimes.isEmpty()) { // 如果mapHeightTimes为空,说明最大高度为0
				mapXvalueHeight.put(nodes[i].x, 0);
			} else { // 如果mapHeightTimes不为空,通过mapHeightTimes.lastKey()取得最大高度
				mapXvalueHeight.put(nodes[i].x, mapHeightTimes.lastKey());
			}
		}
		// res为结果数组,每一个List<Integer>代表一个轮廓线,有开始位置,结束位置,高度,一共三个信息
		List<List<Integer>> res = new ArrayList<>();
		// 一个新轮廓线的开始位置
		int start = 0;
		// 之前的最大高度
		int preHeight = 0;
		// 根据mapXvalueHeight生成res数组
		for (Entry<Integer, Integer> entry : mapXvalueHeight.entrySet()) {
			// 当前位置
			int curX = entry.getKey();
			// 当前最大高度
			int curMaxHeight = entry.getValue();
			if (preHeight != curMaxHeight) { // 之前最大高度和当前最大高度不一样时
				if (preHeight != 0) {
					res.add(new ArrayList<>(Arrays.asList(start, curX, preHeight)));
				}
				start = curX;
				preHeight = curMaxHeight;
			}
		}
		return res;
	}

}

 

上一篇:Apache Hudi重磅特性解读之存量表高效迁移机制


下一篇:级联选择器怎么实现选中某一级时,动态加载下一级