有序表(接口)
红黑树、跳表、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上拿一个