前言
解决问题 == 问题拆解,通过滑动窗口的中位数来完成问题拆解过程,以及linkedHashMap底层实现。
一、滑动窗口中位数
二、问题拆解
package com.xhu.offer.everyday;
import java.util.HashMap;
import java.util.Map;
//滑动窗口中位数
public class MedianSlidingWindow {
/*
target:滑动窗口的中位数?
1-滑动窗口,每次去掉begin对应的数,加入end对应的数,然后begin++,end++,得到动态窗口。
2-窗口中位数,
窗口长度为偶数时,取mid、 mid - 1的值求平均;
窗口长度为奇数时,取mid的值。
M1:带头尾节点的双向链表 + Map<index,Node>来存窗口的有序值。
通过begin index 来删除对应Node,通过顺序查找来插入end index Node。
通过快慢指针来获取中间节点。
时间复杂度控制在O(N)。
*/
public double[] medianSlidingWindow(int[] nums, int k) {
initWindow(nums, 0, k);
double[] rs = new double[nums.length - k + 1];
rs[0] = findMidVal(k & 1);
for (int begin = 1; begin <= nums.length - k; begin++) {
//删begin节点,加入end节点
remove(linkedHashMap.get(begin - 1));
insertVal(nums[begin + k - 1], begin + k - 1);
rs[begin] = findMidVal(k & 1);
}
return rs;
}
/**
* 移除新窗口不需要的数
* @param garbage
*/
private void remove(Node garbage){
//断链
garbage.prev.next = garbage.next;
garbage.next.prev = garbage.prev;
}
/**
* 寻找窗口中的midVal
* @param flag
* @return
*/
private double findMidVal(int flag) {
//快慢指针寻找midValue
Node slow = dummyHead;
Node fast = dummyHead;
while (fast != dummyTail) {
slow = slow.next;
fast = fast.next;
if (fast != dummyTail) fast = fast.next;
}
//根据k为奇偶数来返回平均值
if (1 == flag) return slow.val;
//bug1:两integer value 相加可以溢出
return slow.prev.val / 2.0 + slow.val / 2.0;
}
/**
* 初始化一个k大的窗口
* @param nums
* @param begin
* @param end
*/
private void initWindow(int[] nums, int begin, int end) {
//初始化一个k长有序的双向链表
for (int i = begin; i < end; i++) insertVal(nums[i], i);
}
/**
* 将新数据按升序插入到链表中
* @param num
* @param index
*/
private void insertVal(int num, int index) {
Node cur = dummyHead.next;
//寻找需插入的位置
while (cur != dummyTail && cur.val < num) cur = cur.next;
//生成新节点
Node node = new Node();
node.val = num;
//因为只有一个指针cur,所以为了保证不断链,先操作cur.prev节点。
node.prev = cur.prev;
cur.prev.next = node;
//操作cur节点
node.next = cur;
cur.prev = node;
//Map记录新节点位置。
linkedHashMap.put(index, node);
}
//数据结构:带头尾节点的双向链表 + Map == LinkedHashMap
Node dummyHead = new Node();
Node dummyTail = new Node();
Map<Integer, Node> linkedHashMap = new HashMap<>();
{
//初始化空链表
dummyHead.next = dummyTail;
dummyTail.prev = dummyHead;
}
//双向链表结构
class Node {
int val;//值
Node prev;//前驱指针
Node next;//后继指针
}
}
总结
1)解决问题 == 问题拆解
2)linkedHashMap
参考文献
[1] LeetCode 滑动窗口中位数