滑动数组的相关问题(实时数据流)

来源:互联网 发布:物流仓储软件供应商 编辑:程序博客网 时间:2024/06/16 23:41

实时数据流的这种题,感觉面试的时候还是比较容易问到的,然后专门整理了一下leetcode上的题和剑指offer上的几道题

Leetcode 239. Sliding Window Maximum 滑动集合的最大值
嘀嘀面试的时候遇到了:给一个长度为n的数组,数组中连续k个元素作为一个集合,那么n个数一共可以组成n-k个集合,返回每个集合的最大值。
思路:借用最大队列的思想,维护一个双向队列,保存队列的第一个节点和最后一个节点,集合改变时要把后面的值加入,把前面的值去掉,如果头结点和要去掉的值相同就把头结点往后移,如果不想等就不用改变头结点;新加入的节点如果比最后一个节点小就直接接入,如果比最后一个节点大就一直往前比把比这个值小的节点全都去掉之后把这个值作为一个新节点加进去,新的头结点就是当前集合的最大值。

class DoubleListNode{    int val;    DoubleListNode front,last;    public DoubleListNode(int v){        val = v;    }}public class Solution {    public int[] maxSlidingWindow(int[] nums, int k) {        if(nums.length == 0 || k == 0){            return new int[0];        }        //一个集合中只有一个元素直接返回原数组        if(k == 1) return nums;        int[] maxs = new int[nums.length - k + 1];        DoubleListNode head = new DoubleListNode(nums[0]);        DoubleListNode temp = head;        int i = 0,j = 1;        //只有一个元素的情况        if(nums.length == 1){            maxs[0] = head.val;            return maxs;        }        while(j < nums.length){            //添加元素,大于的情况和小雨的情况            if(temp == null){                temp = new DoubleListNode(nums[j]);            }            if(nums[j] > temp.val){                while(nums[j] > temp.val && temp.front != null){                    temp = temp.front;                }                if(temp.val >= nums[j]){                    DoubleListNode t = new DoubleListNode(nums[j]);                    temp.last = t;                    t.front = temp;                    temp = temp.last;                }                else{                    temp.val = nums[j];                }                temp.last = null;            }            else{                DoubleListNode t = new DoubleListNode(nums[j]);                temp.last = t;                t.front = temp;                temp = temp.last;            }            //需要弹元素            if(j >= k-1){                maxs[i] = head.val;                if(head.val == nums[i]){                    head = head.last;                    if(head != null){                        head.front = null;                    }                }                i++;            }            j++;        }        return maxs;    }}

Leetcode 295. Find Median from Data Stream数据流的中位数(百度面试遇到了)
Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value.
Examples:

[2,3,4] , the median is 3
[2,3], the median is (2 + 3) / 2 = 2.5
Design a data structure that supports the following two operations:
• void addNum(int num) - Add a integer number from the data stream to the data structure.
• double findMedian() - Return the median of all elements so far.
For example:
addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
思路就是实现两个堆,一个大根堆一个小根堆,保持两个堆中元素的满足0<=小根堆个数-大根堆个数<=1,要根据新来的数字的大小和两个堆之前的数目进行调整。
使用优先队列来完成两个堆的动能,调整方式如下:
1) 如果两个堆元素个数相等,且新来的数字比大根堆的堆顶元素大,新来的数字直接进入小根堆,如果新来的数组没有大根堆的堆定元素大,那么将大根堆的顶元素poll之后add进小根堆,将新来的数据加入大根堆;
2) 如果小根堆的元素个数>大根堆的元素个数,且新来的数字比小根堆的堆顶元素大,那么将小根堆的堆顶元素poll之后加入大根堆,将新来的数字加入小根堆,如果新来的数组比小根堆的堆顶元素小,直接加入大根堆。
这样就能保证两个堆的堆定元素一直都是中间的两个数,最后根据数据流目前的元素数来决定结果,如果个数是奇数就直接返回小根堆的堆定,如果个数是偶数就是两个堆定和的二分之一。

public class MedianFinder {    PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();    PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(11,new Comparator<Integer>(){      //i1>i2的话,返回整数是升序,返回负数是降序      public int compare(Integer i1,Integer i2){          return i2-i1;      }      });    int count = 0;    /** initialize your data structure here. */    public MedianFinder() {    }    //minHeap总是比maxHeap的个数多    public void addNum(int num) {        if(minHeap.size() == maxHeap.size()){            if(minHeap.size() > 0 && num < maxHeap.peek()){                minHeap.add(maxHeap.poll());                maxHeap.add(num);            }            else minHeap.add(num);        }        else if(minHeap.size() > maxHeap.size()){            if(num > minHeap.peek()){                maxHeap.add(minHeap.poll());                minHeap.add(num);            }            else                maxHeap.add(num);        }        count ++;    }    public double findMedian() {        if((count & 1) == 1)            return (double)minHeap.peek();        else            return ((double)minHeap.peek() + (double)maxHeap.peek())/2;    }}

Leetcode 480. Sliding Window Median 滑动集合的中位数(其他同学面嘀嘀的时候遇到过)
Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value.
Examples:

[2,3,4] , the median is 3
[2,3], the median is (2 + 3) / 2 = 2.5
Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Your job is to output the median array for each window in the original array.
For example,
Given nums = [1,3,-1,-3,5,3,6,7], and k = 3.
Window position Median
————— —–
[1 3 -1] -3 5 3 6 7 1
1 [3 -1 -3] 5 3 6 7 -1
1 3 [-1 -3 5] 3 6 7 -1
1 3 -1 [-3 5 3] 6 7 3
1 3 -1 -3 [5 3 6] 7 5
1 3 -1 -3 5 [3 6 7] 6
Therefore, return the median sliding window as [1,-1,-1,3,5,6].
和上一道题的思路一样,维护两个堆,上一道题的堆不用出元素,这里面需要写一个出元素的函数

public class Solution {    public double[] medianSlidingWindow(int[] nums, int k) {        PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();        PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(11,new Comparator<Integer>(){          //i1>i2的话,返回整数是升序,返回负数是降序          public int compare(Integer i1,Integer i2){              //不能写成i2-i1的格式,会出现嘴直越界问题              if(i2 > i1) return 1;              else if(i2 < i1) return -1;              else return 0;          }          });        double[] res = new double[nums.length - k + 1];        for(int i = 0; i < nums.length ;i++){            addNum(nums[i],minHeap,maxHeap,i);            if(i >= k - 1 ){                res[i - k + 1] = findMedian(k,minHeap,maxHeap);                removeNum(nums[i - k + 1],minHeap,maxHeap);            }        }        return res;    }    public void removeNum(int num,PriorityQueue<Integer> minHeap,PriorityQueue<Integer> maxHeap){        if(minHeap.size() == maxHeap.size()){            if(minHeap.size() > 0 && num >= minHeap.peek()){                minHeap.remove(num);                minHeap.add(maxHeap.poll());            }             else{                maxHeap.remove(num);            }        }        else if(minHeap.size() > maxHeap.size()){            if(maxHeap.size() > 0 && num <= maxHeap.peek()){                maxHeap.remove(num);                maxHeap.add(minHeap.poll());            }            else                minHeap.remove(num);        }    }    //minHeap总是比maxHeap的个数多    public void addNum(int num,PriorityQueue<Integer> minHeap,PriorityQueue<Integer> maxHeap,int i) {        if(minHeap.size() == maxHeap.size()){            if(minHeap.size() > 0 && num < maxHeap.peek()){                minHeap.add(maxHeap.poll());                maxHeap.add(num);            }            else minHeap.add(num);        }        else if(minHeap.size() > maxHeap.size()){            if(num > minHeap.peek()){                int t = minHeap.poll();                maxHeap.add(t);                minHeap.add(num);            }            else                maxHeap.add(num);        }    }    public double findMedian(int count,PriorityQueue<Integer> minHeap,PriorityQueue<Integer> maxHeap) {        if((count & 1) == 1)            return (double)minHeap.peek();        else            return ((double)minHeap.peek() + (double)maxHeap.peek())/2;    }}

Leetcode 352. Data Stream as Disjoint Intervals(数据流的值区间)
Given a data stream input of non-negative integers a1, a2, …, an, …, summarize the numbers seen so far as a list of disjoint intervals.
For example, suppose the integers from the data stream are 1, 3, 7, 2, 6, …, then the summary will be:
[1, 1]
[1, 1], [3, 3]
[1, 1], [3, 3], [7, 7]
[1, 3], [7, 7]
[1, 3], [6, 7]
实现来一个数字输出区间的功能,如果要时间复杂度是logN的话,那么需要借助平衡二叉查找树的结构,是的每次查询都是logN,TreeMap使用红黑树的思想实现了平衡二叉查找树。

/** * Definition for an interval. * public class Interval { *     int start; *     int end; *     Interval() { start = 0; end = 0; } *     Interval(int s, int e) { start = s; end = e; } * } */public class SummaryRanges {    TreeMap<Integer,Interval> treeMap = new TreeMap<Integer,Interval>();    /** Initialize your data structure here. */    public SummaryRanges() {    }    public void addNum(int val) {        if(treeMap.containsKey(val)) return;        //找到最近的比他大的和 比他小的key        Integer small = treeMap.lowerKey(val);        Integer big = treeMap.higherKey(val);        if(small != null && big != null && treeMap.get(small).end == val - 1 && big == val + 1){            treeMap.get(small).end = treeMap.get(big).end;            treeMap.remove(big);        }        else if(small != null && treeMap.get(small).end + 1 >= val){                int end = Math.max(treeMap.get(small).end ,val);                treeMap.get(small).end = end;        }        else if(big != null && treeMap.get(big).start - 1 <= val){            int start = Math.min(treeMap.get(big).start,val);            treeMap.put(start,new Interval(start,treeMap.get(big).end));            treeMap.remove(big);        }        else{            treeMap.put(val,new Interval(val,val));        }    }    public List<Interval> getIntervals() {        return new ArrayList<>(treeMap.values());    }}

剑指offer 55 字符流中第一个不重复的字符
题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符”go”时,第一个只出现一次的字符是”g”。当从该字符流中读出前六个字符“google”时,第一个只出现一次的字符是”l”。
输出描述:
如果当前字符流没有存在出现一次的字符,返回#字符。
字符一共就256个,用hashMap来存储就行了。插入和输出的时间复杂度都是O(1)

public class Solution {    //Insert one char from stringstream    Map<Character,Integer> map = new HashMap<Character,Integer>();    int index = 0;    public void Insert(char ch)    {        if(map.containsKey(ch))            map.put(ch,-1);        else            map.put(ch,index);        index++;    }  //return the first appearence once char in current stringstream    public char FirstAppearingOnce()    {        int minIndex = Integer.MAX_VALUE;        char res = '#';        for(Character c:map.keySet()){            if(map.get(c) < 0) continue;            if(minIndex > map.get(c)){                minIndex = map.get(c);                res = c;            }        }        return res;    }}