B站左程云算法视频高级班02

有序表(接口)

红黑树、跳表、AVL树、SB树   复杂度O(logN)

题目一:

根据最大高度的变化,maxH,

把原数组改成开始位置增加,结束位置减少的数组

[2,6,8]   改成  [2,add,8],[6,del,8]

[7,11,9] 改成 [7,add,11] , [11,del,9]

[4,8,5] 改成 [4,add,5],[8,del,5]

[1,14,4] 改成 [1,add,4],[14,del,4]

[3,5,3] 改成 [3,add,3],[5,del,3]

根据第一维数据排序,相同的add排在前面(防止意外如[7,7,9])

准备一个map1,key是高度,value是出现的次数

准备一个map2,key是坐标,value是该坐标的最大高度

map1和map2都是有序表

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> {
        public int compara(Node o1, Node o2){
            if (o1.x != o2.x){
                return o1.x - o2.x;
            }
            if(o1.isAdd != o2.isAdd){
                return 01.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++){
        node[i * 2] = new Node(matrix[i][0], true, matrix[i][2]);
        node[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> mapHeight = 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,次数减一即可
                mapHeightTimes.put(nodes[i].h, mapHeightTimes.get(nodes[i],h) - 1);
            }
        }
    //根据mapHeightTimes中的最大高度,设置mapXvalueHeight表
    if(mapHeightTimes.isEmpty()){ //如果maxHeightTimes为空,说明最大高度为0
        mapXHeight.put(node[i].x, 0);
    }else {//如果mapHeightTimes不为空,通过mapHeightTimes.lastKey()取得最大高度
        mapXHeight.put(nodes[i].x, mapHeoghtTimes.lastKey());
    }
    }
    //res为结果数组,每一个List<Integer>代表一个轮廓线,有开始位置,结束位置,高度,一共三个信息
    List<List<Integer>> res =new ArrayList<>();
    //从一个新轮廓线的开始位置
    int start = 0;
    //之前的最大高度
    int preHeight = 0;
    //根据mapXvalueHeight生成res数组
    for(Entry<Integer, Integer> entry : mapXHeight.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;
        }
    }
}

题目二:给定一个数组,该数组无序,但每个值均为正数,再给定一个正数K。求arr的所有子数组中所有元素相加和为k的最长子数组长度

滑动窗口

构建单调性

双指针:L和R

sum < K  R++

sum > K L++

sum == k,收集答案,R++

public static int getMaxLength(int[] arr, int k){
    if(arr == null || arr.length == 0 || k <= 0){
        return 0;
    }
    int left = 0;
    int right = 0;
    //[left...right]
    //如果left == right+ 1表示窗口不再有数
    int sum = arr[0];
    int len = 0;
    while(right < arr.length){
        if(sum == k){
            len = Math.max(len, right - left + 1);
            sum -= arr[left++];
        } else if(sum < k){
            right ++;
            if(right == arr.length){
                break;
            }
            sum += arr[right];
        } else {
            sum -= arr[left++];
        }
    }
    return len;
}

拓展:无序数组有正有负有0,小于等于K的最长子数组长度

arr[ 1 , 2 , 5, -4, 3 , -1]

minsum[1 , 2 , 1, -4 , 2, -1] 从i出发的子数组,取得的最小sum

minsumend[0 , 1 , 3 , 3 , 5 , 5] 从i出发的子数组,取得最小sum时的右边界

对于某位置的数只存在俩种可能性:1)自己 2)往右扩

从0出发扩到一个位置不能扩后,来到1位置,sum-arr[0],看能否把18位置的数扩进来

窗口不回退(只关注窗口能否变长)

题目三:给定一个非负数组,每个值都代表该位置上有几个铜板,a和b玩游戏,a先手,b后手,轮到某个人的时候,只能在一个位置上拿任意数量的铜板,但是不能不拿。谁最先把铜板拿完谁赢,假设a和b都极度聪明,请返回获胜者的名字。

Nim博弈

思路:谁先让对方面对全0的状态谁就赢

数组中所有数异或和起来,不为0先手赢,为0后手赢

   5        2        4

101    010     100    异或后为011

2和4异或后为110,5拿完后小于110不能在5上拿

5和4异或后为001,可以在2上拿一个

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值