手撕代码之“栈”

数据流中的中位数问题

题目描述: 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用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();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值