Gao-yubo

一、大楼轮廓(有序表)

给定一个 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}}

/**
 * @Author: 郜宇博 
 */
public class BuildingBorder {
    public static void main(String[] args) {
        int[][] matrix = new int[][]{
                {2,7,3},
                {3,5,5},
                {4,6,4}
        };
        List<List<Integer>> buildingBorder = getBuildingBorder(matrix);
        System.out.println(buildingBorder);
    }
    /**
     * 用于记录高度变化
     */
    public static class Node{
        public int index;
        public boolean isAdd;
        public int height;
        public Node(int index, boolean isAdd, int height) {
            this.index = index;
            this.isAdd = isAdd;
            this.height = height;
        }
    }
    public static List<List<Integer>> getBuildingBorder(int[][] matrix){
        Node[] nodes = getHeightUpDown(matrix);
        //构造两个有序表
        //坐标-高度表
        TreeMap<Integer,Integer> indexHeightMap = new TreeMap<>();
        //高度-次数表
        TreeMap<Integer,Integer> heightTimesMap = new TreeMap<>();
        fillMap(indexHeightMap,heightTimesMap,nodes);
        //返回结果
        return getBorder(indexHeightMap);
    }
    public static List<List<Integer>> getBorder(TreeMap<Integer,Integer> indexHeightMap){
        List<List<Integer>> res = new ArrayList<>();
        int start = indexHeightMap.firstKey();
        int preHeight = indexHeightMap.get(start);
        for (Map.Entry<Integer,Integer> entry: indexHeightMap.entrySet()){
            //如果高度发生变化
            if (entry.getValue() != preHeight){
                //添加(start,curKey,preHeight)
                List<Integer> border= null;
                if (preHeight != 0){
                    border = new ArrayList<>(Arrays.asList(start,entry.getKey(),preHeight));
                    res.add(border);
                }
                //新的list,起始点,高度都变了
                start = entry.getKey();
                preHeight = entry.getValue();
            }
        }
        return res;
    }
    /**
     * 填充map表
     */
    public static void fillMap(TreeMap<Integer,Integer> indexHeightMap,TreeMap<Integer,Integer> heightTimesMap,Node[] nodes){
        for (Node node: nodes){
            //是否为添加
            if (node.isAdd){
                //1.处理heightTimesMap
                //添加过,次数+1
                if (heightTimesMap.containsKey(node.height)){
                    heightTimesMap.put(node.height,heightTimesMap.get(node.height)+1);
                }else {
                    //没添加guo
                    heightTimesMap.put(node.height,1);
                }
            }else {
                //下降
                if (heightTimesMap.get(node.height)==1){
                    //删除后为0,则移除
                    heightTimesMap.remove(node.height);
                }else {
                    heightTimesMap.put(node.height,heightTimesMap.get(node.height)-1);
                }
            }
            //2.处理indexHeightMap
            //没有高度了,到结尾了
            //获取最大高度
            Integer maxHeight =  heightTimesMap.isEmpty()?0:heightTimesMap.lastKey();
            indexHeightMap.put(node.index,maxHeight);
        }
    }
    /**
     * 获取高度变化的数组,返回排序后的结果
     */
    public static Node[] getHeightUpDown(int[][] matrix){
        Node[] resultNode = new Node[matrix.length * 2];
        for (int i  = 0; i < matrix.length; i++){
            for (int j = 0; j < matrix[0].length ; j++){
                resultNode[i * 2] = new Node(matrix[i][0],true,matrix[i][2]);
                resultNode[i * 2+1] = new Node(matrix[i][1],false,matrix[i][2]);
            }
        }
        //按照index大小排序,相同的话根据add在前,排序,防止出现线状楼
        Arrays.sort(resultNode,(x,y)->{
            if (x.index != y.index){
                return x.index - y.index;
            }
            if (x.isAdd != y.isAdd){
                return x.isAdd?-1:1;
            }
            return 0;
        });
        return resultNode;
    }
}

二、最长子数组等于k(构造单调性、滑动窗口)

给定一个数组 arr,该数组无序,但每个值均为正数,再给定一个正数 k。

求 arr 的 所有子 数组中所有元素相加和为 k 的最长子数组长度。

例如,arr=[1,2,1,1,1],k=3。

累加和为 3 的最长子数组为[1,1,1],所以结果返回 3。
要求:时间复杂度O(N),额外空间复杂度O(1)

/**
 * @Author: 郜宇博
 * 给定一个数组 arr,该数组无序,但每个值均为正数,再给定一个正数 k。
 * 求 arr 的 所有子 数组中所有元素相加和为 k 的最长子数组长度。
 */
public class LongestChildArraySumK {
    public static void main(String[] args) {
        int len = 30;
        int k = 15;
        int[] arr = generatePositiveArray(len);
        System.out.println(getLongestCount(arr, k));
    }

    /**
     * 构造单调性,因为数组都是正数,保持一个区间,
     * 当区间内sum < k时,继续扩充区间
     * 当区间sum = k 时,以i为起点,此区间最大了,更换区间,r++, l++
     * 当sum > k时,如果l,r在同一位置,都向后移动,不在的话,left++,缩小区域
     */
    public static int getLongestCount(int[] array, int target){
        int left = 0;
        int right = 0;
        // sum =  [left,...,right]
        int sum = array[0];
        int result = Integer.MIN_VALUE;
        while (right != array.length){
            //sum = array[right];
            if (sum == target){
                result = Math.max(result, right - left + 1);
                if (right == array.length-1){
                    return Math.max(result, 0);
                }
                sum = sum - array[left++] + array[++right];
            }
            else if (sum < target){
                if (right == array.length-1){
                    return Math.max(result, 0);
                }
                sum += array[++right];
            }else {
                if (left == right){
                    sum = array[++left];
                    right++;
                }else {
                    sum -= array[left++];
                }
            }
        }
        return Math.max(result, 0);
    }
    public static int[] generatePositiveArray(int size) {
        int[] result = new int[size];
        for (int i = 0; i != size; i++) {
            result[i] = (int) (Math.random() * 10) + 1;
        }
        return result;
    }
}

三、拿硬币

给定一个非负数组,每一个值代表该位置上有几个铜板。

a和b玩游戏,a先手,b后手, 轮到某个人的时候,只能在一个位置上拿任意数量的铜板,但是不能不拿。

谁最先把铜板拿完谁赢。假设a和b都极度聪明,请返回获胜者的名字。

/**
 * @Author: 郜宇博
 * 给定一个非负数组,每一个值代表该位置上有几个铜板。
 * a和b玩游戏,a先手,b后手, 轮到某个人的时候,只能在一个位置上拿任意数量的铜板,但是不能不拿。
 * 谁最先把铜板拿完谁赢。假设a和b都极度聪明,请返回获胜者的名字。
 */
public class PickMoneyWin {
    public static void main(String[] args) {
        int[] array = new int[]{3,4,1};
        System.out.println(getWinner(array));
    }
    /**
     * 最后桌子上剩下1个硬币时,先手赢,否则后手赢
     * 将所有数字求异或和,为0说明先手怎么都赢不了
     */
    public static String getWinner(int[] array){
        int eorResult = 0;
        for (int num: array){
            eorResult ^= num;
        }
        return eorResult==0?"后手":"先手";
    }
}

四、最长子数组小于等于k(舍去部分解)

给定一个无序数组 arr,其中元素可正、可负、可 0,给定一个整数 k。

求 arr 所 有的子数组 中累加和小于或等于 k 的最长子数组长度。

例如:arr=[3,-2,-4,0,6],k=-2,相加和小于或等于-2 的最长子数组为{3,-2,-4,0},所以结果返回4。

/**
 * @Author: 郜宇博
 * 给定一个无序数组 arr,其中元素可正、可负、可0,给定一个整数k。
 * 求arr 所有的子数组累加和小于或等于k的最长子数组长度。
 */
public class LongestChildArraySumLessK {
    public static void main(String[] args) {
        int[] array = new int[]{3,-2,-4,0,6};
        System.out.println(getSumLessK(array,-2));
    }
    public static int getSumLessK(int[] array, int target) {
        //准备两个数组
        //1.以i开始,向后最小sum
        //2.到达最小sum的边界坐标
        int[] minSum = new int[array.length];
        int[] minBorder = new int[array.length];
        minSum[array.length - 1] = array[array.length - 1];
        minBorder[array.length - 1] = array.length - 1;
        for (int i = array.length - 2; i >= 0; i--) {
            minSum[i] = array[i];
            minBorder[i] = i;
            //如果右边的最小和<0,那么应该向右拓展
            if (minSum[i + 1] <= 0) {
                minSum[i] += minSum[i + 1];
                minBorder[i] = minBorder[i + 1];
            }
        }
        int left = 0;
        int right = 0;
        int result = 0;
        int curSum = 0;
        for (; array.length - left > result; left++) {
            //不越界,并且满足条件
            while (right < array.length && minSum[right] + curSum <= target) {
                //更新curSum
                curSum += minSum[right];
                right = minBorder[right] + 1;
            }
            //找到了以left为左边界最长满足条件的数组,更新
            result = Math.max(result,right - left);
            //边界整体向右移动(!重点)
            if (left == right){
                right++;
            }else {
                curSum -= array[left];
            }
        }
        return result;
    }
}

分类:

算法

技术点:

相关文章: