题目一
奖励自己一个手撕并查集
class Node{
int value;
public Node(int v) {
value = v;
}
}
class unionfind{
HashMap<Integer, Node> nodes = new HashMap<Integer, Node>();//用来保存所有节点 你得通过value找到对应点
HashMap<Node, Node> father = new HashMap<Node, Node>();//所有节点对应的父节点
HashMap<Node, Integer> size = new HashMap<Node, Integer>();//保存每个节点对应的 这一条的 size
public void add(int [] arr) {//初始化并查集
for(int i = 0;i<arr.length;i++) {
Node node = new Node(arr[i]);
nodes.put(arr[i],node);
father.put(node, node);//每个节点的父亲都是它自己
size.put(node, 1);//每个父亲的为头的串 长度只有1 只需要记录父节点的长度就行了 其他的都是冗余的
}
}
public void union(Node node1,Node node2) {//肯定是吧短的连在长的上
Node fa1 = father.get(node1);
Node fa2 = father.get(node2);
int size1 = size.get(fa1);
int size2 = size.get(fa2);
/* if(size1>=size2) {
father.put(fa2, fa1);
size.put(fa1,size1+size2);
size.remove(fa2);
}else {
father.put(fa1, fa2);
size.put(fa2,size1+size2);
size.remove(fa1);
} 没考虑相等点的情况 如果是同一个点的话 那么size会变成2 很明显不合理*/
if(node1!=node2) {
Node bigger = size1>size2?fa1:fa2;
Node smaller = bigger==node1?node2:node1;
father.put(smaller,bigger);
size.put(bigger, size1+size2);
size.remove(smaller);
}
}
public Node findfather(Node cur) {
Stack<Node> stack = new Stack<Node>();
while(cur!=father.get(cur)){
stack.add(cur);
cur = father.get(cur);
}
while(!stack.isEmpty()) {
father.put(stack.pop(), cur);
}
return cur;
}
public int getsize(Node node) {
return size.get(father.get(node));
}
}
题目二
Dijkstra算法
class Node {
public int value;
public int in;
public int out;
public ArrayList<Node> nexts;
public ArrayList<Edge> edges;
public Node(int value) {
this.value = value;
in = 0;
out = 0;
nexts = new ArrayList<>();
edges = new ArrayList<>();
}
}
class Edge {
public int weight;
public Node from;
public Node to;
public Edge(int weight, Node from, Node to) {
this.weight = weight;
this.from = from;
this.to = to;
}
}
class NodeRecord {
public Node node;
public int distance;
public NodeRecord(Node node, int distance) {
this.node = node;
this.distance = distance;
}
}
class Myheap{
Node [] nodes;
HashMap<Node, Integer> indexMap;//对标一般堆结构中的 位置map 这里面没有的是 用值去找对应点(点本身压根没有值啊) 这个相当于有node了找位置 那有位置找点怎么办? 直接数组啊
HashMap<Node, Integer> distanceMap;
int size;
public Myheap(int size) {
this.size = size;
nodes = new Node [size];
indexMap = new HashMap<Node, Integer>();
distanceMap = new HashMap<Node, Integer>();
}
public void heapify(int pos) {
int left = indexMap.get(pos)*2+1;
while(left<size) {
int smallest = left;
if(left+1<size) {
smallest = distanceMap.get(nodes[left])>distanceMap.get(nodes[left+1])?left+1:left;
}
if(distanceMap.get(nodes[pos])>distanceMap.get(nodes[smallest])) {
swap(pos,smallest);
}else {
break;
}
pos = smallest;//pos = left;是不对的因为可能和left+1的换 所以是smallest
left = smallest*2+1;
}
}
public void insert(int pos) {
while (distanceMap.get(nodes[pos]) < distanceMap.get(nodes[(pos - 1) / 2])) {//当pos为0时(无论是1 还是 2 再减1除以2都为零 所以无论是左边上来还是右边上来都会有这一步) 0-1/2还等于0 也就是说所以到0点的时候就一定等于 因为就是一个node
swap(pos, (pos - 1) / 2);
pos = (pos - 1) / 2;
}
}
public void swap(Node node1,Node node2) {
int pos1 = indexMap.get(node1);
int pos2 = indexMap.get(node2);
Node tmp = node1;
node1 = node2;
node2 = tmp;
indexMap.put(node1, pos2);
indexMap.put(node2, pos1);
}
public void swap(int pos1,int pos2) {
indexMap.put(nodes[pos1], pos2);
indexMap.put(nodes[pos2], pos1);
Node tmp = nodes[pos1];
nodes[pos1] = nodes[pos2];
nodes[pos2] = tmp;
}
public boolean isentered(Node node) {//进过堆
return indexMap.containsKey(node);
}
public boolean inheap(Node node) {//在堆中 区别就是 堆弹出的元素 就是进过堆 但是不在堆中
return indexMap.containsKey(node)&&(indexMap.get(node)!=-1);//后面我们会看到 把弹出节点的位置(map)中变成-1 但是不删除它 来表示它进过堆 但是呢 又出去了
}
public NodeRecord pop() {
NodeRecord record = new NodeRecord(nodes[0], distanceMap.get(nodes[0]));
swap(0, size - 1);
indexMap.put(nodes[size-1], -1);
distanceMap.remove(nodes[size - 1]);//这个不比index 用不到了就删除了吧 不删除还影响找最小distance
nodes[--size] = null;//破案了 这个数组中删除一个点 就把他换到最后 置空 然后size--(这个不能忘记)
heapify(0);//把被交换的向下调整
return record;
}
public void addorupdateorIgnore(Node node,int distance) {
if(inheap(node)) {
distanceMap.put(node,Math.min(distance, distanceMap.get(node)));
insert(indexMap.get(node));
}
if(!isentered(node)) {
nodes[size] = node; //注意 size只能在最后++ 要不这个size就不表示它的位置了
indexMap.put(node, size);//只要给他加进去 之后位置的调整是insert的事
distanceMap.put(node, distance);
insert(size++);
}
}//在堆中更新 进过堆了但是被弹出就是判断完了 忽略 没进过堆add
}
public static HashMap<Node, Integer> dijkstra(Node head,int size) {
HashMap<Node, Integer> res = new HashMap<Node, Integer>();
Myheap heap = new Myheap(size);
res.put(head, 0);
while(heap.size!=0) {
NodeRecord record = heap.pop();
Node node = record.node;
int distance = record.distance;
for(Edge edge:node.edges) {
heap.addorupdateorIgnore(edge.to, edge.weight+distance);//这个distance是 拿出的点的已积累路径 再加上到达当前点的权重(边上的另一头) 然后具体行为交给addorupdateorIgnore判断
}
res.put(node, distance); //对应的是 每次找一个最小的点(肯定是能到达的) 这个点看完之后就不再看了 所以把它弹出放进结果了(弹出就表示不要了)
}
return res;
}
题目三
假设一个固定大小为W的窗口,依次划过arr,返回每一次滑出状况的最大值例如,arr=[4,3,5,4,3,3,6,7],W=3返回:[5,5,5,4,6,7]
这要是看不出来滑动窗口法就自裁罢
说一下思路 往右扩 就是如果满足从大到小 就正常加入 如果不满足 就从后往前删 直到可以满足了再加入
原理是啥呢 我们演示一遍
4 加入
3 加入
5 弹出4 3 加入
此时窗口正式形成
4 加入
左侧缩一个 看看这个位置上的数在不在窗口里 不在了已经弹出来 那不管了(所以窗口里面保存的是数组元素的位置)
3加入
左侧缩一个 看看原来的在不在 不在 不管
3加入
左侧缩一个 看看原来的在不在 在 弹出...以此类推
每次这个窗口的第一个位置就是它的窗口最大值
原理是怎么回事呢 我们窗口中的元素的含义是:成为最大值的优先级 假如我们此时窗口数值为[7,4,3] 那就是说7是最优先成为最大的 然后是4... 当然这是正常情况 窗口后面的数不会坐以待毙
如果下一个数是8 那不好意4 3 在8存在的时间内都成不了最大值 最变态的是什么 这个8在4 3 的后面出现 4 3都弹出去也轮不到8 永无出头之日了这回(所以可以直接把不满足从大到小的都弹出去吧 那只要前面有小的 永远都不会成为最大值了看都不用看)
OK 我们开始coding
public int[] maxSlidingWindow(int[] nums, int k) {
int length = nums.length;
int [] res = new int [length-k+1];
int index = 0;
/*Stack<Integer> stack = new Stack<Integer>();
int index = 0;
int R = 0;//窗口大小固定 所以有R就有L
for(;R<k;R++) {
while(!stack.isEmpty()&&nums[stack.peek()]<nums[R]) {
stack.pop();
}
stack.push(R);
} 不能用栈实现啊 那第一个值取不出来啊 */
LinkedList<Integer> list = new LinkedList<Integer>();
int R = 0;
for(;R<k;R++) {
while (!list.isEmpty()&&nums[list.peekLast()]<nums[R]) {//因为栈可以用双向链表实现 所以里面有栈的方法 这很合理
list.pollLast();//不过pop弹出的是队首元素哦
}
list.addLast(R);
}
res[index++]= nums[list.getFirst()];
while(R<length) {
while (!list.isEmpty()&&nums[list.peekLast()]<nums[R]) {
list.pollLast();
}
if(list.contains(R-k)) {//contains就是正常的是否包含某元素 但是remove是移除某位置元素
list.pollFirst();//因为R先向右移动了一个了
}
list.addLast(R++);
res[index++]= nums[list.getFirst()];//一定要放在左移和右扩之后啊 放中间肯定不对
}
return res;
}
做完这道题 我最大的感受就是 虽然双向链表可以模拟实现栈 但是不要用它的栈方法 乱套 只用addFIrst addLast pollFirst polllast 对于到底是前面加入还是后面加入来说这就很清晰