第一题 用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push
、pop
、peek
、empty
):
实现 MyQueue
类:
void push(int x)
将元素 x 推到队列的末尾int pop()
从队列的开头移除并返回元素int peek()
返回队列开头的元素-
boolean empty()
如果队列为空,返回true
;否则,返回false
说明:
- 你 只能 使用标准的栈操作 —— 也就是只有
push to top
,peek/pop from top
,size
, 和is empty
操作是合法的。 - 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
双栈
一个输入栈,一个输出栈,因为栈的特点是先入后出(FILO),队列是先入先出(FIFO),那么使用两个栈,一个栈用于“输入”,一个栈用于“输出”,输出当要出队或访问队首时输入栈弹出元素压入输入栈。
class MyQueue {
Stack<Integer> stackIn;
Stack<Integer> stackOut;
public MyQueue() {
stackIn = new Stack<>(); // 输入栈
stackOut = new Stack<>(); // 输出栈
}
public void push(int x) {
stackIn.push(x);
}
public int pop() {
dumpstackIn();
return stackOut.pop();
}
public int peek() {
dumpstackIn();
return stackOut.peek();
}
public boolean empty() {
return stackIn.isEmpty() && stackOut.isEmpty();
}
// 如果stackOut为空,那么将stackIn中的元素全部放到stackOut中
private void dumpstackIn(){
if (!stackOut.isEmpty())
return;
while (!stackIn.isEmpty()){
stackOut.push(stackIn.pop());
}
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
第二题 用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push
、top
、pop
和 empty
)。
实现 MyStack
类:
void push(int x)
将元素 x 压入栈顶。int pop()
移除并返回栈顶元素。int top()
返回栈顶元素。boolean empty()
如果栈是空的,返回true
;否则,返回false
。
双队列
添加元素先加到que2后面,然后把que1的元素都备份到que2,再交换que2和que1,这时第一个元素位于que1队首,相当于栈顶元素,通过que2添加元素,que1删除元素或者读取元素,从而实现栈的功能。
class MyStack {
Queue<Integer> queue1; // 和栈中保持一样元素的队列
Queue<Integer> queue2; // 辅助队列
/** Initialize your data structure here. */
public MyStack() {
queue1 = new LinkedList<>();
queue2 = new LinkedList<>();
}
/** Push element x onto stack. */
public void push(int x) {
queue2.offer(x); // 先放在辅助队列中
while (!queue1.isEmpty()){
queue2.offer(queue1.poll());
}
Queue<Integer> queueTemp;
queueTemp = queue1;
queue1 = queue2;
queue2 = queueTemp; // 最后交换queue1和queue2,将元素都放到queue1中
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
return queue1.poll(); // 因为queue1中的元素和栈中的保持一致,所以这个和下面两个的操作只看queue1即可
}
/** Get the top element. */
public int top() {
return queue1.peek();
}
/** Returns whether the stack is empty. */
public boolean empty() {
return queue1.isEmpty();
}
}
单队列
和上面双队列的逻辑相同,但是是在自身上操作,把自己的队首加到队尾,从而让要加入的元素放到队首,实现栈的“后入先出”。
class MyStack {
Queue<Integer> queue;
public MyStack() {
queue = new LinkedList<>();
}
//每 offer 一个数(A)进来,都重新排列,把这个数(A)放到队列的队首
public void push(int x) {
queue.offer(x);
int size = queue.size();
//移动除了 A 的其它数
while (size-- > 1)
queue.offer(queue.poll());
}
public int pop() {
return queue.poll();
}
public int top() {
return queue.peek();
}
public boolean empty() {
return queue.isEmpty();
}
}
双向队列
因为题目要求,不能通过双向队列中栈的功能去实现栈。
Deque 接口继承了 Queue 接口,所以 Queue 中的 add、poll、peek等效于 Deque 中的 addLast、pollFirst、peekFirst。
因此,使用Deque和Qeque没啥差别。
第三题 队列的实现
链队列
基于链表实现的队列
public class LinkedListQuque {
public class ListNode { // 链表节点
int val;
ListNode next;
ListNode(){}
ListNode(int val){
this.val = val;
}
}
private ListNode front, rear; // 头,尾
private int queSize = 0; // 长度
public LinkedListQuque() { // 构造器
front = null;
rear = null;
}
public void offer(int val) { // 入队
ListNode node = new ListNode(val);
if (front == null) {
front = node;
rear = node;
} else {
rear.next = node;
rear = rear.next;
}
queSize++;
}
public int poll() { // 出队
int n;
if (front == null) {
throw new IndexOutOfBoundsException();
} else {
n = front.val;
front = front.next;
queSize--;
}
return n;
}
}
顺序队列
基于环形数组实现的队列
因为数组中删除首元素的时间效率太低,所以通过“取余操作”实现环形数组。
要想扩容,可以添加动态数组。
package com.practiceA;
public class ArrayQuque {
private int[] nums;
private int front; // 指向队首
private int queSize;
public ArrayQuque(int capacity) { // 构造器
nums = new int[capacity];
front = queSize = 0;
}
public void offer(int val) { // 入队
if (queSize == nums.length) {
System.out.println("满了");
return;
}
// 取余是为了后面满了能回到头部
int rear = (front + queSize) % nums.length;
queSize++;
}
public int poll(boolean isFront) { // 出队
if (queSize == 0) {
throw new IndexOutOfBoundsException();
}
int val = nums[front];
// 为了后面满时能回到头部
front = (front + 1) % nums.length;
queSize--;
return val;
}
}
链式双向队列
基于双向链表实现的双向队列
public class LinkedListDuque {
public class ListNode { // 链表节点
int val;
ListNode next, prev;
ListNode(){}
ListNode(int val){
this.val = val;
}
}
private ListNode front, rear; // 头,尾
private int queSize = 0; // 长度
public LinkedListDuque() { // 构造器
front.next = rear;
rear.prev = front;
}
public void addFirst(int num) {
push(num, true);
}
public void addLast(int num) {
push(num, false);
}
public void removeFirst(int num) {
pop(true);
}
public void removeLast(int num) {
pop(false);
}
private void push(int val, boolean isFront) { // 入队
ListNode node = new ListNode(val);
if (isEmpty()) {
front = node;
rear = node;
} else if (isFront) { // 队首入队
node.next = front;
front.prev = node;
front = node;
} else { // 队尾入队
rear.next = node;
node.prev = rear;
rear = node;
}
queSize++;
}
private int pop(boolean isFront) { // 出队
int n;
if (isEmpty()) {
throw new IndexOutOfBoundsException();
} else if (isFront){ // 队首出队
n = front.val;
front = front.next;
queSize--;
} else {
n = rear.val;
rear = rear.prev;
queSize--;
}
return n;
}
public boolean isEmpty() {
return queSize == 0;
}
}
数组双向队列
基于环形数组实现
public class ArrayDuque {
private int[] nums;
private int front; // 指向队首
private int queSize;
public ArrayDuque(int capacity) { // 构造器
nums = new int[capacity];
front = queSize = 0;
}
// 计算索引
private int index(int i) {
return (i + nums.length) % nums.length;
}
public void offerFirst(int val) { // 队首入队
if (queSize == nums.length) {
System.out.println("满了");
return;
}
// 队首的前一位
front = index(front - 1);
nums[front] = val;
queSize++;
}
public void offerLast(int val) { // 队尾入队
if (queSize == nums.length) {
System.out.println("满了");
return;
}
// 队尾的后一位
int rear = index(front + queSize);
nums[rear] = val;
queSize++;
}
public int pollFirst(boolean isFront) { // 队首出队
if (queSize == 0) {
throw new IndexOutOfBoundsException();
}
int val = nums[front];
// 队首后一位
front = index(front + 1);
queSize--;
return val;
}
public int pollLast(boolean isFront) { // 队尾出队
if (queSize == 0) {
throw new IndexOutOfBoundsException();
}
// 队尾
int rear = index(front + queSize - 1);
int val = nums[rear];
// 队尾会被覆盖,当他不存在
queSize--;
return val;
}
}
第四题 循环队列的实现
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应该支持如下操作:
MyCircularQueue(k)
: 构造器,设置队列长度为 k 。Front
: 从队首获取元素。如果队列为空,返回 -1 。Rear
: 获取队尾元素。如果队列为空,返回 -1 。enQueue(value)
: 向循环队列插入一个元素。如果成功插入则返回真。deQueue()
: 从循环队列中删除一个元素。如果成功删除则返回真。isEmpty()
: 检查循环队列是否为空。isFull()
: 检查循环队列是否已满。
链表
class MyCircularQueue {
private ListNode front, rear;
private int queSize = 0; // 长度
private int capacity = 0; // 容量
// 构造器,设置队列长度为 k
public MyCircularQueue(int k) {
// 循环链表
front = null;
rear = null;
capacity = k;
}
// 向循环队列插入一个元素。如果成功插入则返回真
public boolean enQueue(int value) {
if (isFull()) {
return false;
}
ListNode node = new ListNode(value);
if (front == null) {
rear = front = node;
} else {
// 队尾插
rear.next = node;
rear = node;
rear.next = front;
}
queSize++;
return true;
}
// 从循环队列中删除一个元素。如果成功删除则返回真
public boolean deQueue() {
if (isEmpty()) {
return false;
}
// 当被删到只有一个时只写else里面的会出错
if (queSize == 1) {
front = rear = null;
} else {
// 队头删
front = front.next;
rear.next = front;
}
queSize--;
return true;
}
// 从队首获取元素。如果队列为空,返回 -1
public int Front() {
if (isEmpty()) {
return -1;
} else {
return front.val;
}
}
// 获取队尾元素。如果队列为空,返回 -1
public int Rear() {
if (isEmpty()) {
return -1;
} else {
return rear.val;
}
}
// 检查循环队列是否为空
public boolean isEmpty() {
return queSize == 0;
}
// 检查循环队列是否已满
public boolean isFull() {
return queSize == capacity;
}
}
/**
* Your MyCircularQueue object will be instantiated and called as such:
* MyCircularQueue obj = new MyCircularQueue(k);
* boolean param_1 = obj.enQueue(value);
* boolean param_2 = obj.deQueue();
* int param_3 = obj.Front();
* int param_4 = obj.Rear();
* boolean param_5 = obj.isEmpty();
* boolean param_6 = obj.isFull();
*/
环形数组
class MyCircularQueue {
private int[] nums;
private int front, rear;
private int queSize = 0; // 长度
private int capacity = 0; // 容量
// 构造器,设置队列长度为 k
public MyCircularQueue(int k) {
nums = new int[k];
capacity = k;
front = rear = 0;
}
// 计算环形数组索引
private int index(int i) {
return (i + capacity) % capacity;
}
// 向循环队列插入一个元素。如果成功插入则返回真
public boolean enQueue(int value) {
if (isFull()) {
return false;
}
rear = index(front + queSize);
nums[rear] = value;
queSize++;
return true;
}
// 从循环队列中删除一个元素。如果成功删除则返回真
public boolean deQueue() {
if (isEmpty()) {
return false;
}
front = index(front + 1);
queSize--;
return true;
}
// 从队首获取元素。如果队列为空,返回 -1
public int Front() {
if (isEmpty()) {
return -1;
} else {
return nums[front];
}
}
// 获取队尾元素。如果队列为空,返回 -1
public int Rear() {
if (isEmpty()) {
return -1;
} else {
return nums[rear];
}
}
// 检查循环队列是否为空
public boolean isEmpty() {
return queSize == 0;
}
// 检查循环队列是否已满
public boolean isFull() {
return queSize == capacity;
}
}
第四题 有效的括号
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
解一
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (stack.isEmpty()) {
stack.push(s.charAt(i));
continue;
}
if (stack.peek() == ')' || stack.peek() == ']' || stack.peek() == '}') {
return false;
} else if (stack.peek() == '(') {
if (c == ')') {
stack.pop();
} else {
stack.push(c);
}
} else if (stack.peek() == '[') {
if (c == ']') {
stack.pop();
} else {
stack.push(c);
}
} else if (stack.peek() == '{') {
if (c == '}') {
stack.pop();
} else {
stack.push(c);
}
}
}
return stack.isEmpty();
}
}
解二
class Solution {
public boolean isValid(String s) {
int n = s.length();
// 不对称,直接返回
if (n % 2 == 1) {
return false;
}
// 使用匿名内部类的方式初始化了一个HashMap实例,并添加了三个键值对
Map<Character, Character> pairs = new HashMap<Character, Character>() {{
put(')', '(');
put(']', '[');
put('}', '{');
}};
Stack<Character> stack = new Stack<Character>();
for (int i = 0; i < n; i++) {
char ch = s.charAt(i);
// 遇到右括号出栈
if (pairs.containsKey(ch)) {
if (stack.isEmpty() || stack.peek() != pairs.get(ch)) {
return false;
}
stack.pop();
} else { // 左括号入栈
stack.push(ch);
}
}
return stack.isEmpty();
}
}
解三
class Solution {
public boolean isValid(String s) {
if (s.isEmpty())
return true;
Stack<Character> stack = new Stack<>();
for (char c : s.toCharArray()) {
if (c == '(')
stack.push(')');
else if (c == '{')
stack.push('}');
else if (c == '[')
stack.push(']');
else if (stack.empty() || c != stack.pop()) {
return false;
}
}
return stack.isEmpty();
}
}
第五题 每日温度
给定一个整数数组 temperatures
,表示每天的温度,返回一个数组 answer
,其中 answer[i]
是指对于第 i
天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0
来代替。
栈
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int length = temperatures.length;
int[] answer = new int[length];
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < length; i++) {
// 当前温度比栈顶温度高,进入循环
while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
// 找到栈顶温度对应的更高温度
int prevIndex = stack.pop();
answer[prevIndex] = i - prevIndex;
}
// 当前温度比栈顶温度低,找不到更高温度
// 要么更高温度在后面,要不找不到更高温度,默认值为0
// 当前栈内温度从栈底到栈顶依次降低,所以当找到栈顶的更高温度时,栈可以遍历完毕
stack.push(i);
}
return answer;
}
}
非栈
速度比用栈快
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] ans = new int[n];
ans[n - 1] = 0;
for (int i = n - 2; i >= 0; i--) {
int j = i + 1;
// ans[j] 即temperatures[j]对应更高温度比temperatures[j]多的天数
// ans[j] 等于0时,说明temperatures[j]后面找不到更高温度了
// temperatures[i]比temperatures[j]高,那就得找更高的temperatures[ans[j]]
while (temperatures[j] <= temperatures[i] && ans[j] != 0) {
j += ans[j];
}
// 找到更高温度
if (temperatures[j] > temperatures[i]) {
ans[i] = j - i;
}
// 找不到更高温度,即temperatures[j] <= temperatures[i] && ans[j] == 0
// 数组默认值0
}
return ans;
}
}
错误解
没用栈,超时了,有一个测试案例是前面一堆99,最后写个100,得逆序才行
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int[] answer = new int[temperatures.length];
for(int i = 0; i < temperatures.length; i++) {
if (i == temperatures.length - 1) {
answer[i] = 0;
break;
}
int count = 1; // 记录天数
int j = i + 1;
if (temperatures[i] <= temperatures[j]) {
while (j < temperatures.length) {
count++;
if (temperatures[i] > temperatures[j]) {
answer[i] = count;
break;
}
j++;
}
} else if (temperatures[i] > temperatures[j]) {
answer[i] = count;
}
}
return answer;
}
}
第六题 删除字符串中的所有重复项
给出由小写字母组成的字符串 S
,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
栈
class Solution {
public String removeDuplicates(String s) {
Stack<Character> stack = new Stack<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// i == 0时直接入栈
// 不这样写报错访问空栈
if (i == 0 || stack.isEmpty()) {
stack.push(c);
} else if (stack.peek() == c) {
stack.pop();
} else {
stack.push(c);
}
}
StringBuilder sb = new StringBuilder();
for (Character c : stack) {
sb.append(c);
}
return sb.toString();
// 直接写输出的是"[c, a]",而题目要求"ca"
// return stack.toString();
}
}
双指针法
图片结合代码
class Solution {
public String removeDuplicates(String s) {
char[] chars = s.toCharArray();
int slow = 0, fast = 0;
while(fast < s.length()){
chars[slow] = chars[fast];
// 跳过第一位
// 如果chars[slow]等于它的前一位,让它等于slow[fast]
if( slow > 0 && chars[slow] == chars[slow - 1]){
slow--;
}else{
slow++;
}
fast++;
}
return new String(chars).substring(0,slow);
}
}
第七题 逆波兰表达式求和
给你一个字符串数组 tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
栈
两个答案一样
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i];
if (token.equals("+")) {
int num2 = stack.pop();
int num1 = stack.pop();
stack.push(num1 + num2);
} else if (token.equals("-")) {
int num2 = stack.pop();
int num1 = stack.pop();
stack.push(num1 - num2);
} else if (token.equals("*")) {
int num2 = stack.pop();
int num1 = stack.pop();
stack.push(num1 * num2);
} else if (token.equals("/")) {
int num2 = stack.pop();
int num1 = stack.pop();
stack.push(num1 / num2);
} else {
stack.push(Integer.parseInt(token));
}
}
return stack.peek();
}
}
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i];
if (isNumber(token)) {
stack.push(Integer.parseInt(token));
} else {
int num2 = stack.pop();
int num1 = stack.pop();
switch (token) {
case "+" -> {
stack.push(num1 + num2);
}
case "-" -> {
stack.push(num1 - num2);
}
case "*" -> {
stack.push(num1 * num2);
}
case "/" -> {
stack.push(num1 / num2);
}
default ->{}
}
}
}
return stack.peek();
}
public boolean isNumber(String token) {
return !("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token));
}
}
第九题 滑动窗口最大值
给你一个整数数组 nums
,有一个大小为 k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k
个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
自定义数组(单调队列)
class MyQueue {
Deque<Integer> deque = new LinkedList<>();
// 记录要弹出元素是否等于队列出口的元素
// 即,如果要弹出的应该当前的最大值,那么就弹出,否则不弹出
void poll (int val) {
if (!deque.isEmpty() && val == deque.peek())
deque.poll();
}
// 添加元素时,弹出从后往前所有队尾元素比val小的数,再加入val
// 这样就保证了最大值始终在队首,队列内元素依次减小
void add (int val) {
while (!deque.isEmpty() && val > deque.getLast())
deque.removeLast();
deque.add(val);
}
// 队首,即最大值
int peek() {
return deque.peek();
}
}
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
// 先排除特殊情况
if (nums.length == 1)
return nums;
// 返回的数组
int[] res = new int[nums.length - k + 1];
// 记录最大值索引
int num = 0;
MyQueue que = new MyQueue();
// 前k个元素
for (int i = 0; i < k; i++)
que.add(nums[i]);
res[num++] = que.peek();
// 后面的元素
for (int i = k; i < nums.length; i++) {
que.poll(nums[i - k]); // 窗口最左侧
que.add(nums[i]);
res[num++] = que.peek();
}
return res;
}
}
双向队列(单调队列)
和上个答案类似,但是代码更简洁,运行更快。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
ArrayDeque<Integer> deque = new ArrayDeque<>();
int[] res = new int[nums.length - k + 1];
int index = 0;
for (int i = 0; i < nums.length; i++) {
// 队列头结点在窗口内,否则弹出
while (!deque.isEmpty() && deque.peek() < i - k + 1)
deque.poll();
// 单调,即保证每次放进去的数字都比末尾大
while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i])
deque.pollLast();
deque.offer(i);
// 窗口每滑一格,都将队首所对应元素放入结果中
if (i >= k - 1)
res[index++] = nums[deque.peek()];
}
return res;
}
}
优先队列(堆)
还没学。
第十题 前k个高频元素
给你一个整数数组 nums
和一个整数 k
,请你返回其中出现频率前 k
高的元素。你可以按 任意顺序 返回答案。
大顶堆
优先级队列 -> 披着队列外衣的堆
堆 -> 一颗完全二叉树,树中每个节点的值都不大于(或不小于)其左右孩子的值
大顶堆 (堆头最大)
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
// key -> 元素值 value -> 出现次数
for (int num : nums)
map.put(num, map.getOrDefault(num, 0) + 1);
// 创建了优先级队列,其中元素是整数数组
// 其比较器(Comparator)是根据数组的第二个元素(即value)降序排序
PriorityQueue<int[]> pq = new PriorityQueue<>((p1, p2) -> p2[1] - p1[1]);
// 将map中的元素存入优先级队列
for (Map.Entry<Integer, Integer> entry : map.entrySet())
// 大顶堆排序
pq.add(new int[] {entry.getKey(), entry.getValue()});
int[] ans = new int[k];
for (int i = 0; i < k; i++)
ans[i] = pq.poll()[0];
return ans;
}
}
小顶堆
小顶堆(堆头最小)
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
// key -> 元素值 value -> 出现次数
for (int num : nums)
map.put(num, map.getOrDefault(num, 0) + 1);
// 创建了优先级队列,其中元素是整数数组
// 其比较器(Comparator)是根据数组的第二个元素(即value)降序排序
PriorityQueue<int[]> pq = new PriorityQueue<>((p1, p2) -> p1[1] - p2[1]);
// 将map中的元素存入优先级队列
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
// 小顶堆排序
// 队列长度不足k位,直接加
if (pq.size() < k)
pq.add(new int[] {entry.getKey(), entry.getValue()});
else // 长度到了k
// 如果当前元素出现次数比队首元素(即最少次数的元素)多
// 那么直接弹出队首,将该元素插入队列
if (entry.getValue() > pq.peek()[1]) {
pq.poll();
pq.add(new int[] {entry.getKey(), entry.getValue()});
}
// 没影响,那就跳过
}
int[] ans = new int[k];
for (int i = k - 1; i >= 0; i--)
ans[i] = pq.poll()[0];
return ans;
}
}
第十一题 根据身高重建队列
假设有打乱顺序的一群人站成一个队列,数组 people
表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki]
表示第 i
个人的身高为 hi
,前面 正好 有 ki
个身高大于或等于 hi
的人。
请你重新构造并返回输入数组 people
所表示的队列。返回的队列应该格式化为数组 queue
,其中 queue[j] = [hj, kj]
是队列中第 j
个人的属性(queue[0]
是排在队列前面的人)。
思路:先按身高从大到小排,然后按k依次往前插
排序
class Solution {
public int[][] reconstructQueue(int[][] people) {
// 排序规则是先比较每个子数组的第一个元素
// 不相等降序排列;相等,则比较第二个元素,按照升序排列
Arrays.sort(people, new Comparator<int[]>() {
public int compare (int[] p1, int[] p2) {
if (p1[0] != p2[0])
return p2[0] - p1[0];
else
return p1[1] - p2[1];
}
});
List<int[]> ans = new ArrayList<int[]>();
for (int[] p : people)
ans.add(p[1], p);
return ans.toArray(new int[ans.size()][]);
}
}
简化
class Solution {
public int[][] reconstructQueue(int[][] people) {
// 排序规则是先比较每个子数组的第一个元素
// 不相等降序排列;相等,则比较第二个元素,按照升序排列
Arrays.sort(people, (x, y) -> x[0] == y[0] ? x[1] - y[1] : y[0] - x[0]);
List<int[]> ans = new ArrayList<int[]>();
for (int[] p : people)
ans.add(p[1], p);
return ans.toArray(new int[0][0]);
}
}
目录