系列汇总:《刷题系列汇总》
文章目录
——————《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 平均值(太多,有空看)
-
题目描述:给你两个整数
m
和k
,以及数据流形式的若干整数。你需要实现一个数据结构,计算这个数据流的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(会员)
- 题目描述:
- 优秀思路: