【刷题系列】队列

系列汇总:《刷题系列汇总》



——————《LeectCode》———————

1. 设计循环队列

  • 题目描述:设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。(请不要使用内置的队列库)
  • 优秀思路:利用数组实现
public class MyCircularQueue {
	private int[] data; // 循环队列的长度固定,直接用数组即可
	private int head; //头元素
	private int tail; //尾元素
	private int size; //大小

	public MyCircularQueue(int k) { // 构造方法
		data = new int[k];
		head = -1;
		tail = -1;
		size = k;
	}

	/** 将元素插入循环队列。如果操作成功,则返回true */
	public boolean enQueue(int value) { // 插入到尾部
		// 如果队列已满,则无法成功添加新元素,返回false
		if (isFull()) {
			return false;
		}
		if (isEmpty()) {
			head = 0;
		}
		tail = (tail + 1) % size; // 计算下一个tail的值:原tail后一位
		data[tail] = value;
		return true;
	}

	/** 从循环队列中删除一个元素。如果操作成功,则返回true */
	public boolean deQueue() { // 删除头元素
		// 如果队列为空,则无法成功删除元素,返回false
		if (isEmpty()) {
			return false;
		}
		if (head == tail) { // 只有一个元素的情况
			head = -1;
			tail = -1;
		} else {
			head = (head + 1) % size; // head往后移动1
		}
		return true;
	}

	/** 从队首获取元素。如果队列为空,返回 -1 */
	public int Front() {
		if (isEmpty()) {
			return -1;
		}
		return data[head];
	}

	/** 获取队尾元素。如果队列为空,返回 -1 */
	public int Rear() {
		if (isEmpty()) {
			return -1;
		}
		return data[tail];
	}

	/** 检查循环队列是否为空 */
	public boolean isEmpty() {
		return head == -1;
	}

	/** 检查循环队列是否为满 */
	public boolean isFull() {
		return (tail + 1) % size == head;
	}
}


2. 和至少为 K 的最短子数组(没看懂)

  • 题目描述:返回 A 的最短的非空连续子数组的长度,该子数组的和至少为 K 。如果没有和至少为 K 的非空子数组,返回 -1
  • 优秀思路:【单调队列】
class Solution {
    public int shortestSubarray(int[] A, int K) {
        int n = A.length;
        int[] sum = new int[n + 1];
        for (int i=1;i<=n;i++) sum[i] = sum[i-1] + A[i-1];

        Deque<Integer> q = new LinkedList<>();
        int minLength = Integer.MAX_VALUE;
        for (int i=0;i<=n;i++) {

            while (!q.isEmpty() && sum[q.peekLast()] >= sum[i]) q.pollLast();

            while (!q.isEmpty() && sum[i] - sum[q.peekFirst()] >= K) {

                minLength = Math.min(minLength, i - q.pollFirst());
            }
            q.addLast(i);
        }
        if (minLength == Integer.MAX_VALUE) return -1;
        else return minLength;
    }
}

3. 设计循环双端队列

  • 题目描述:设计实现双端队列。你的实现需要支持以下操作:
    • MyCircularDeque(k):构造函数,双端队列的大小为k。
    • insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true。
    • insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。
    • deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。
    • deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。
    • getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。
    • getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。
    • isEmpty():检查双端队列是否为空。
    • isFull():检查双端队列是否满了。
  • 我的优秀思路(96%):利用数组实现
class MyCircularDeque {
    int size = 0;
    int tail = -1;
    int[] queue;

    /** 构造函数,双端队列的大小为k */
    public MyCircularDeque(int k) {
        size = k;
        queue = new int[k];
    }
    
    /** 将一个元素添加到双端队列头部。 如果操作成功返回 true。 */
    public boolean insertFront(int value) {
        if(isFull()) return false;
        for(int i = tail;i >= 0;i--){ // 必须倒序赋值,否则前面的值会被覆盖
            queue[i+1] = queue[i];
        }
        queue[0] = value;
        tail++;
        return true;
    }
    
    /** 将一个元素添加到双端队列尾部。如果操作成功返回 true。 */
    public boolean insertLast(int value) {
        if(isFull()) return false;
        tail++;
        queue[tail] = value;
        return true;
    }
    
    /** 从双端队列头部删除一个元素。 如果操作成功返回 true。 */
    public boolean deleteFront() {
        if(isEmpty()) return false;
        for(int i = 1;i <= tail;i++){
            queue[i-1] = queue[i];
        }
        queue[tail] = 0;
        tail--;
        return true;
    }
    
    /** 从双端队列尾部删除一个元素。如果操作成功返回 true。 */
    public boolean deleteLast() {
        if(isEmpty()) return false;
        queue[tail] = 0;
        tail--;
        return true;
    }
    
    /** 从双端队列头部获得一个元素。如果双端队列为空,返回 -1。*/
    public int getFront() {
        if(isEmpty()) return -1;
        return queue[0];
    }
    
    /** 获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。*/
    public int getRear() {
        if(isEmpty()) return -1;
        return queue[tail];
    }
    
    /** 检查双端队列是否为空 */
    public boolean isEmpty() {
        if(tail == -1) return true;
        return false;
    }
    
    /** 检查双端队列是否满了。 */
    public boolean isFull() {
        if(tail == size - 1) return true;
        return false;
    }
}

4.求出 MK 平均值(太多,有空看)

  • 题目描述:给你两个整数 mk ,以及数据流形式的若干整数。你需要实现一个数据结构,计算这个数据流的 MK 平均值 。MK 平均值 按照如下步骤计算:

    如果数据流中的整数少于 m 个,MK 平均值 为 -1 ,否则将数据流中最后 m 个元素拷贝到一个独立的容器中。
    从这个容器中删除最小的 k 个数和最大的 k 个数。
    计算剩余元素的平均值,并 向下取整到最近的整数

    请你实现 MKAverage 类:
    - MKAverage(int m, int k) 用一个空的数据流和两个整数 m 和 k 初始化 MKAverage 对象。
    - void addElement(int num) 往数据流中插入一个新的元素 num 。
    - int calculateMKAverage() 对当前的数据流计算并返回 MK 平均数 ,结果需 向下取整到最近的整数 。

    在这里插入图片描述

  • 优秀思路:使用LinkedList Q保存最近的m的数据值。自定义TreeMap为MyTreeMap,并生成若干个(一般会是三个,即第一个存放m个数里最小的k个,最后一个存放最大的k个;如果还有,那么中间的myTreeMap存放m个数里中间的m-2×k个数);这若干(一般3个)个myTreeMap,存放在myTreeMapList里。

class MKAverage {
    class MyTreeMap {
        private final TreeMap<Integer, Integer> treeMap;
        private final int capacity;
        private int size;
        private int sum;

        public MyTreeMap(int capacity) {
            treeMap = new TreeMap<>();
            this.capacity = capacity;
        }

        public boolean remove(int num) {
            if (treeMap.containsKey(num)) {
                int numCnt = treeMap.get(num);
                if (--numCnt > 0) {
                    treeMap.put(num, numCnt);
                } else {
                    treeMap.remove(num);
                }
                sum -= num;
                size--;
                return true;
            }
            return false;
        }

        public Integer pollFirstEntry() {
            if (treeMap.size() > 0) {
                int num = treeMap.firstKey();
                remove(num);
                return num;
            }
            return null;
        }

        public Integer add(int num) {
            //加入num,如果size超出capacity,去除最大的一个数
            int numCnt = treeMap.getOrDefault(num, 0);
            treeMap.put(num, numCnt + 1);
            sum += num;
            size++;
            if (size > capacity) {
                Map.Entry<Integer, Integer> lastEntry = treeMap.lastEntry();
                if (lastEntry.getValue() > 1) {
                    treeMap.put(lastEntry.getKey(), lastEntry.getValue() - 1);
                } else {
                    treeMap.remove(lastEntry.getKey());
                }
                sum -= lastEntry.getKey();
                size--;
                return lastEntry.getKey();
            }
            return null;
        }

        public int avg() {
            return sum / size;
        }
    }

    int m;
    int k;
    private final LinkedList<Integer> Q;
    private final List<MyTreeMap> myTreeMapList;


    public MKAverage(int m, int k) {
        this.m = m;
        this.k = k;
        Q = new LinkedList<>();
        myTreeMapList = new ArrayList<>();
        myTreeMapList.add(new MyTreeMap(k));
        if (m - 2 * k > 0) {
            myTreeMapList.add(new MyTreeMap(m - 2 * k));
        }
        myTreeMapList.add(new MyTreeMap(k));
    }

    // 删除num,结果总是使myTreeMapList的小、中、大三个treemap依次填充。(先保证最小的treeMap填充、再保证中间的treeMap填充、最后是最大的填充)
    private void removeElement(int num) {
        boolean removed = false;
        for (int i = 0; i < myTreeMapList.size(); i++) {
            if (!removed) {
                removed = myTreeMapList.get(i).remove(num);
            } else {
                Integer minNum = myTreeMapList.get(i).pollFirstEntry();
                if (minNum == null) {
                    break;
                }
                myTreeMapList.get(i - 1).add(minNum);
            }
        }
    }

    public void addElement(int num) {
        if (Q.size() == m) {
            int numToRem = Q.removeFirst();
            removeElement(numToRem);
        }
        Q.addLast(num);
        Integer numToAdd = num;
        for (int i = 0; i < myTreeMapList.size() && numToAdd != null; i++) {
            numToAdd = myTreeMapList.get(i).add(numToAdd);
        }
    }

    public int calculateMKAverage() {
        if (Q.size() < m || myTreeMapList.size() < 3) {
            return -1;
        }
        return myTreeMapList.get(1).avg();
    }
}

5. 最近的请求次数

  • 题目描述:写一个 RecentCounter 类来计算特定时间范围内最近的请求。请你实现 RecentCounter 类,保证 每次对 ping 的调用都使用比之前更大的 t 值。
    • RecentCounter() 初始化计数器,请求数为 0 。
    • int ping(int t) 在时间 t 添加一个新请求,其中 t 表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在 [t-3000, t] 内发生的请求数。
  • 我的优秀思路(92%):队列实现
class RecentCounter {
    Queue<Integer> time;
    public RecentCounter() {
        time = new LinkedList<>();
    }
    
    public int ping(int t) {
        time.add(t);
        while(time.peek() < t-3000){
            time.poll();
        }
        return time.size();
    }
}

6. 346(会员)

  • 题目描述
  • 优秀思路

7. 353(会员)

  • 题目描述
  • 优秀思路

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值