【漫步计算机系统】之数据结构与算法(10):堆和队列

问题一给定一个数组,找出其中最小的K个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。

代码如下:

public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {    if (k > nums.length || k <= 0)        return new ArrayList<>();    PriorityQueue<Integer> maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);    for (int num : nums) {        maxHeap.add(num);        if (maxHeap.size() > k)            maxHeap.poll();    }    return new ArrayList<>(maxHeap);}

算法描述:

  1. 从头至尾迭代数组;

  2. 将数组元素压入最大堆maxHeap;

  3. 若压入的数量超出了K,弹出堆的根元素,即队中最大值元素;

  4. 迭代结束,堆maxHeap中保存的即为最小的K个元素。

问题二:得到一个数据流中的中位数

代码如下:

/* 大顶堆,存储左半边元素 */private PriorityQueue<Integer> left = new PriorityQueue<>((o1, o2) -> o2 - o1);/* 小顶堆,存储右半边元素,并且右半边元素都大于左半边 */private PriorityQueue<Integer> right = new PriorityQueue<>();/* 当前数据流读入的元素个数 */private int N = 0;
public void Insert(Integer val) {    /* 插入要保证两个堆存于平衡状态 */    if (N % 2 == 0) {        /* N 为偶数的情况下插入到右半边。         * 因为右半边元素都要大于左半边,但是新插入的元素不一定比左半边元素来的大,         * 因此需要先将元素插入左半边,然后利用左半边为大顶堆的特点,取出堆顶元素即为最大元素,此时插入右半边 */        left.add(val);        right.add(left.poll());    } else {        right.add(val);        left.add(right.poll());    }    N++;}
public Double GetMedian() {    if (N % 2 == 0)        return (left.peek() + right.peek()) / 2.0;    else        return (double) right.peek();}

算法描述:

  1. 设置一个大顶堆left和小顶堆right;

  2. 当数据流的个数为偶数,将输入的数插入大顶堆left,并将大顶堆的根元素弹出插入小顶堆right;

  3. 当数据流的个数为奇数,将输入的数插入小顶堆right,并将大顶堆的根元素弹出插入大顶堆left;

  4. 这样right里面的数都比left里面的数大,若数据流个数为偶数,要获得中位数大小,取left和right根元素的平均值;

  5. 若数据流个数为奇数,此时right里元素比left里刚好多一个,要获得中位数大小,取right根元素值即可。

问题三:字符流中第一个不重复的字符

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符 "go" 时,第一个只出现一次的字符是 "g"。当从该字符流中读出前六个字符“google" 时,第一个只出现一次的字符是 "l"。

代码如下:

private int[] cnts = new int[128];private Queue<Character> queue = new LinkedList<>();
public void Insert(char ch) {    cnts[ch]++;    queue.add(ch);    while (!queue.isEmpty() && cnts[queue.peek()] > 1)        queue.poll();}
public char FirstAppearingOnce() {    return queue.isEmpty() ? '#' : queue.peek();}

算法描述:

  1. 定义一个128位的整型数组cnts(每个元素代表一个ASCII码字符),以及一个字符型队列queue;

  2. 每输入一个字符,该字符对应的cnts位元素值加1;

  3. 将该字符入队列queue;

  4. 从头至尾迭代队列queue,若队列字符对应的cnts位元素值大于1,队列头元素弹出;

  5. 此时队列的头元素即为字符流中第一个不重复的字符。

问题四:滑动窗口的最大值

例如,如果输入数组 {2, 3, 4, 2, 6, 2, 5, 1} 及滑动窗口的大小 3,那么一共存在 6 个滑动窗口,他们的最大值分别为 {4, 4, 6, 6, 6, 5}。

代码如下:

public ArrayList<Integer> maxInWindows(int[] num, int size) {    ArrayList<Integer> ret = new ArrayList<>();    if (size > num.length || size < 1)        return ret;    PriorityQueue<Integer> heap = new PriorityQueue<>((o1, o2) -> o2 - o1);  /* 大顶堆 */    for (int i = 0; i < size; i++)        heap.add(num[i]);    ret.add(heap.peek());    for (int i = 0, j = i + size; j < num.length; i++, j++) {            /* 维护一个大小为 size 的大顶堆 */        heap.remove(num[i]);        heap.add(num[j]);        ret.add(heap.peek());    }    return ret;}

算法描述:

  1. 设置一个最大堆heap,窗口大小为size;

  2. 先将数组中前size个元素入堆中,得到根元素为第一个窗口的最大值,入最大值链表;

  3. 将窗口第一个元素从堆中删除,即将窗口向前移动一位;窗口最右边的新元素入堆中,再将此时堆中根元素入最大值链表;

  4. 迭代至窗口滑动到最右边,返回最大值链表,程序结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HIT_USTC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值