数据流中的中位数问题
题目描述: 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
题目的分析: 这道题目要使用到PriorityQueue优先级队列,我们先来介绍优先级队列,它是一种特殊的队列,只不过在出队列的时候,从队首出队的元素有个优先级别(也即总是出队最大值,或总是出队最小值),这个可以设置:
- new PriorityQueue((a,b)->a-b);队列内部是升序排序(默认),即队首的元素永远为当前值的最小值,所以是minHeap;
- new PriorityQueue((a,b)->b-a);队列内部是降序排序,即队首元素为最大值,所以是:maxHeap;
由于不知道数据动态加入,且数据的长度的奇偶性不确定,我们可以使用一个count来奇数,同时为了保证数据能动态均匀地加入到maxHeap和minHeap中,我们约定:当count为奇数时,数据加入maxHeap,否则加入minHeap,为了保证maxHep和minHeap的队首原属为中位数,我们需要保证待加入的元素num必须与要符合:maxHeap队首元素<=num<=minHeap队首元素,可以参见下图来辅助理解:
如图所示:待插入元素是5,按照上述约定(插入元素为奇数个,插入maxHeap中),需要保证5插入到maxHeap之后符合:“maxHeap中队首元素<=minHeap队首的元素”,如果满足直接插入,否者,需要作如下调整:先将5插入到minHep中(minHeap内部将会自动降序排列,此时取出的minHeap队首的元素必然符合上述条件),偶数时,同样的道理,具体可以参考代码,更好理解
import java.util.*;
public class Solution {
//创建大顶堆(用于升序排序),lambada表达式实现comparator比较器
Queue<Integer> maxHeap=new PriorityQueue<Integer>((a,b)->b-a);
Queue<Integer> minHeap=new PriorityQueue<Integer>((a,b)->a-b);
//记录数据的总个数,奇偶分别处理
//为了保证数据能均匀分配道两个堆中去,可以约定,count为偶数时候
//先入maxHeap,否则入,minHeap;同时需要保证:maxHeap的最大元素<=minHeap中的最小元素
int count=-1;
public void Insert(Integer num) {
count++;
if(count%2==0){//偶数入maxHeap,入之前先判断
if(!minHeap.isEmpty() && num>minHeap.peek()){
minHeap.add(num);
num=minHeap.poll();
}
maxHeap.add(num);
}else{//奇数
if(!maxHeap.isEmpty() && num<maxHeap.peek()){
//保证,加入到minHeap中的元素num不能小于maxHeap.peek(),否则要交换
maxHeap.add(num);
num=maxHeap.poll();//内部自动升序排序
}
minHeap.add(num);
}
}
public Double GetMedian() {
//取出的同样要分奇、偶性质
if(maxHeap.size()==minHeap.size()){
//取两个优先机队列的顶部元素的平均值
return (maxHeap.peek()+minHeap.peek())/2.0;
}else if(maxHeap.size()>minHeap.size()){
return maxHeap.peek()/1.0;
}else{
return minHeap.peek()/1.0;
}
}
}
判断栈的入栈顺序和出栈顺序是否一致
题目描述:
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
解题步骤:
(1)特判:当popA和pushA数组的长度有一个为空,必然不一致
(2)先将pushA中的第一个元素入栈。
(3)设置两个指针i和j分别指向pushA和popA,判断stack栈顶的元素是否和popA[j]元素相同,如果相同,则出栈,j指向下一位,如果不同,则循环执行(3)直到相同为止,若i的索引超出步骤,则表明他们顺序不会一致,返回false;
直接看代码
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
public boolean IsPopOrder(int [] pushA,int [] popA) {
if(pushA.length==0 || popA.length==0){
return false;
}
Stack<Integer> stack=new Stack<Integer>();
int i=0;//pushA的指针
int j=0;//popA的指针;
stack.push(pushA[i++]);
while(j<popA.length){
while(stack.peek()!=popA[j]){
//继续入栈直到popA[j]元素相同为止
if(i<pushA.length){
stack.push(pushA[i++]);
}else{
return false;
}
}
//i<pushA.length && peek()==pop[j]
//出栈
stack.pop();
j++;
}
return true;
}
}
包含Min函数的栈结构
题目描述:
定义一个栈结构,该栈中有个能获取当前栈中最小元素的函数min,要求算法的时间复杂度为O(1)
解题思路:
这道题是典型的空间换时间的思想,要定义两个栈,一个栈表示本体stack1,另一个栈stakc2存储当前栈中的最小值,在入栈时,需要判断待入栈的元素和stack2栈顶元素的大小,只有value<=stack2.peek()时,才入stack2,这样就保证stack2的栈顶一定为当前栈的最小元素。出栈时,如果stack2和stack1的栈顶元素相同,则stack2中的元素也必须同时更新出栈。
直接看代码
import java.util.Stack;
public class Solution {
Stack<Integer> stack1=new Stack<Integer>();//本身栈
Stack<Integer> stack2=new Stack<Integer>();//用于保存最小值
public void push(int node) {
stack1.push(node);
if(stack2.isEmpty()){
stack2.push(node);
}else if(stack2.peek()>=node){
//stack2中只保存最小值
stack2.push(node);
}
}
public void pop() {
if(stack1.peek()==stack2.peek()){
stack2.pop();
}
stack1.pop();
}
public int top() {
return stack1.peek();
}
public int min() {
return stack2.peek();
}
}