note

栈与队列理论1

The queue data structure follows the FIFO (First In First Out) principle.

A stack follows the LIFO (Last In First Out) principle, i.e., the element inserted at the last is the first element to come out.

stack: push pop peek

*

1
2
3
4
5
stack.push(1);
stack.push(2);
stack.peek(); // 返回 1
stack.pop(); // 返回 1
stack.isEmpty(); // 返回 false
  • stack.push() stack.peek()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String args[])
{
// Creating an empty Stack
Stack<String> stack = new Stack<String>();

// Use push() to add elements into the Stack
stack.push("Welcome");
stack.push("To");
stack.push("Geeks");
stack.push("For");
stack.push("Geeks");

// Displaying the Stack
System.out.println("Initial Stack: " + stack);

// Fetching the element at the head of the Stack
System.out.println("The element at the top of the"
+ " stack is: " + stack.peek());

// Displaying the Stack after the Operation
System.out.println("Final Stack: " + stack);
}

Output:

1
2
3
Initial Stack: [Welcome, To, Geeks, For, Geeks]
The element at the top of the stack is: Geeks
Final Stack: [Welcome, To, Geeks, For, Geeks]
  • stack.pop()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class StackDemo {
public static void main(String args[])
{
// Creating an empty Stack
Stack<Integer> stack = new Stack<Integer>();

// Use add() method to add elements
stack.push(10);
stack.push(15);
stack.push(30);
stack.push(20);
stack.push(5);

// Displaying the Stack
System.out.println("Initial Stack: " + stack);

// Removing elements using pop() method
System.out.println("Popped element: "
+ stack.pop());
System.out.println("Popped element: "
+ stack.pop());

// Displaying the Stack after pop operation
System.out.println("Stack after pop operation "
+ stack);
}
}

Output:

1
2
3
4
Initial Stack: [10, 15, 30, 20, 5]
Popped element: 5
Popped element: 20
Stack after pop operation [10, 15, 30]
  • stack.isEmpty()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.Stack;

public class StackExample {
public static void main(String[] args) {
// Create a new stack
Stack<Integer> stack = new Stack<>();

// Push elements onto the stack
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);

// Pop elements from the stack
while(!stack.isEmpty()) {
System.out.println(stack.pop());
}
}
}

Queue: offer poll peek

https://www.programiz.com/java-programming/queue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Queue<String> animal1 = new LinkedList<>();

add() - Inserts the specified element into the queue. If the task is successful, add() returns true, if not it throws an exception.

offer() - Inserts the specified element into the queue. If the task is successful, offer() returns true, if not it returns false.



element() - Returns the head of the queue. Throws an exception if the queue is empty.

peek() - Returns the head of the queue. Returns null if the queue is empty.


remove() - Returns and removes the head of the queue. Throws an exception if the queue is empty.
poll() - Returns and removes the head of the queue. Returns null if the queue is empty.

contains mehtod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import java.util.LinkedList;
import java.util.Queue;

public class QueueContainsExample {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();

// Add elements to the queue
queue.offer("First");
queue.offer("Second");
queue.offer("Third");

// Check if the queue contains a specific element
String elementToCheck = "Second";
boolean containsElement = queue.contains(elementToCheck);

if (containsElement) {
System.out.println("The queue contains " + elementToCheck);
} else {
System.out.println("The queue does not contain " + elementToCheck);
}
}
}




import java.util.Stack;

public class StackContainsExample {
public static void main(String[] args) {
Stack<String> stack = new Stack<>();

// Push elements onto the stack
stack.push("First");
stack.push("Second");
stack.push("Third");

// Check if the stack contains a specific element
String elementToCheck = "Second";
boolean containsElement = stack.contains(elementToCheck);

if (containsElement) {
System.out.println("The stack contains " + elementToCheck);
} else {
System.out.println("The stack does not contain " + elementToCheck);
}
}
}

Deque:

Deque deque = new LinkedList<>();

addFirst addLast

removeFirst removeLast

getFirst getLast

deque.size( ) deque.isEmpty()

In Java, a Deque (Double-Ended Queue) is an interface that provides various methods to perform basic operations on a double-ended queue, which allows you to add and remove elements from both ends (front and rear) of the queue. The LinkedList class is a commonly used implementation of the Deque interface. Here are some basic operations you can perform with a Deque in Java:

  1. Adding Elements:
    • addFirst(E e) or offerFirst(E e): Adds an element to the front of the deque.
    • addLast(E e) or offerLast(E e): Adds an element to the end (rear) of the deque.
1
2
3
Deque<Integer> deque = new LinkedList<>();
deque.addFirst(1);
deque.addLast(2);
  1. Removing Elements: !!!!
    • removeFirst() or pollFirst(): Removes and returns the element at the front of the deque. Returns null if the deque is empty (for pollFirst).
    • removeLast() or pollLast(): Removes and returns the element at the end (rear) of the deque. Returns null if the deque is empty (for pollLast).
1
2
int front = deque.removeFirst();
int rear = deque.pollLast();
  1. **Accessing Elements: **!!!
    • getFirst() or peekFirst(): Returns the element at the front of the deque without removing it. Returns null if the deque is empty (for peekFirst).
    • getLast() or peekLast(): Returns the element at the end (rear) of the deque without removing it. Returns null if the deque is empty (for peekLast).
1
2
int front = deque.getFirst();
int rear = deque.peekLast();
  1. Size and Empty Checks:
    • size(): Returns the number of elements in the deque.
    • isEmpty(): Checks if the deque is empty.
1
2
int size = deque.size();
boolean isEmpty = deque.isEmpty();
  1. Iteration:

    You can iterate over the elements in a deque using an enhanced for loop or an iterator.

1
2
3
4
5
6
7
8
9
for (Integer element : deque) {
System.out.println(element);
}

Iterator<Integer> iterator = deque.iterator();
while (iterator.hasNext()) {
Integer element = iterator.next();
System.out.println(element);
}

These are some of the basic operations you can perform on a Deque in Java. Depending on your needs, you can choose the appropriate methods to manipulate the double-ended queue as a stack, queue, or both.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Java program to demonstrate the working
// of a Deque in Java

import java.util.*;

public class DequeExample {
public static void main(String[] args)
{
Deque<String> deque
= new LinkedList<String>();

// We can add elements to the queue
// in various ways

// Add at the last
deque.add("Element 1 (Tail)");

// Add at the first
deque.addFirst("Element 2 (Head)");

// Add at the last
deque.addLast("Element 3 (Tail)");

// Add at the first
deque.push("Element 4 (Head)");

// Add at the last
deque.offer("Element 5 (Tail)");

// Add at the first
deque.offerFirst("Element 6 (Head)");

System.out.println(deque + "\n");

// We can remove the first element
// or the last element.
deque.removeFirst();
deque.removeLast();
System.out.println("Deque after removing "
+ "first and last: "
+ deque);
}
}

Output

1
2
3
[Element 6 (Head), Element 4 (Head), Element 2 (Head), Element 1 (Tail), Element 3 (Tail), Element 5 (Tail)]

Deque after removing first and last: [Element 4 (Head), Element 2 (Head), Element 1 (Tail), Element 3 (Tail)]

232. Implement Queue using Stacks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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() {
clearStack();
return stackOut.pop();
}

public int peek() {
clearStack();
return stackOut.peek();
}

public boolean empty() {
return stackIn.isEmpty() && stackOut.isEmpty();
}

//notice!!!!!
private void clearStack(){ // double stacks, inmotating queue
//在push数据的时候,只要数据放进输入栈就好,但在pop的时候,操作就复杂一些,输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入),再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。
if(stackOut.isEmpty()) { //important!!! stackout 全部搬空了以后再进数据,不然顺序会乱
while(!stackIn.isEmpty()){ //important
//make sure stackIn is always emtpy, everytime there's new element added to the stack it will be added to stackOut imediately
stackOut.push(stackIn.pop());
}
}

}
}

225. Implement Stack using Queues

Implement the MyStack class:

  • void push(int x) Pushes element x to the top of the stack.
  • int pop() Removes the element on the top of the stack and returns it.
  • int top() Returns the element on the top of the stack.
  • boolean empty() Returns true if the stack is empty, false otherwise.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class MyStack {
Queue<Integer> que;

public MyStack() {
que = new LinkedList<>();
}
//每 offer 一个数(A)进来,都重新排列,把这个数(A)放到队列的队首, notic 每字
public void push(int x) {
que.offer(x);
int size = que.size();
size = size - 1; //!!!!!
//移动除了 A 的其它数
while(size > 0){
que.offer(que.poll());
size--;
}
}

public int pop() {
return que.poll();
}

public int top() {
return que.peek();
}

public boolean empty() {
return que.isEmpty();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// use two queues:   i feel like this method is harder to think
class MyStack {
Queue<Integer> que1;
Queue<Integer> que2;

public MyStack() {
que1 = new LinkedList<>();
que2 = new LinkedList<>();
}

public void push(int x) {
//que2 would always be empty at the end
que2.add(x);
while(!que1.isEmpty()){
que2.add(que1.poll());
}
Queue<Integer> temp;
temp = que1;
que1=que2;
que2 = temp;
}

public int pop() {
return que1.poll();
}

public int top() {
return que1.peek();
}

public boolean empty() {
return que1.isEmpty();
}
}

20. Valid Parentheses

Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.

An input string is valid if:

  1. Open brackets must be closed by the same type of brackets.
  2. Open brackets must be closed in the correct order.
  3. Every close bracket has a corresponding open bracket of the same type.

there’s three situations that doesn’t consent to the restricts:

  1. 第一种情况,字符串里左方向的括号多余了 ,所以不匹配。 括号匹配1
  2. 第二种情况,括号没有多余,但是 括号的类型没有匹配上。 括号匹配2
  3. 第三种情况,字符串里右方向的括号多余了,所以不匹配。 括号匹配3

我们的代码只要覆盖了这三种不匹配的情况,就不会出问题,可以看出 动手之前分析好题目的重要性。

动画如下:

20.有效括号

第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false

第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false

第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public boolean isValid(String s) {
Deque<Character> que = new LinkedList<>(); // it's a stack here
for(int i = 0; i<s.length(); i++){
char ch = s.charAt(i);
if(ch == '('){
que.push(')');
}else if(ch == '{'){
que.push('}');
}else if(ch == '['){
que.push(']');
}else if(que.isEmpty() || que.peek() != ch){
// notice here, isEmpty -> check this situation "]", "(){}}{" 避免多了右方向括号
return false;
}else{
que.pop();
}
}
return que.isEmpty(); // the reasone that here cant say return true is -> "([]"避免多了左方向的括号
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//second try
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
char[] arr = s.toCharArray();
for(int i = 0 ; i < arr.length; i++){
if(arr[i] == '{' || arr[i] =='[' || arr[i] == '('){
stack.push(arr[i]);
}else{
if(stack.isEmpty()) return false;
char temp = stack.peek();
if(temp == '{' && arr[i] == '}'){
stack.pop();
}
else if( temp == '(' && arr[i] == ')' ){
stack.pop();
}
else if( temp == '[' && arr[i] == ']'){
stack.pop();
}else{
return false;
}
}
}

return stack.isEmpty();
}
}

1047. Remove All Adjacent Duplicates In String

Easy

You are given a string s consisting of lowercase English letters. A duplicate removal consists of choosing two !adjacent! and equal letters and removing them.

We repeatedly make duplicate removals on s until we no longer can.

Return the final string after all such duplicate removals have been made. It can be proven that the answer is unique.

Example :

1
2
Input: s = "azxxzy"
Output: "ay"

1047.删除字符串中的所有相邻重复项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
class Solution {
public String removeDuplicates(String s) {
Deque<Character> que= new LinkedList<>();
char c;
for(int i = 0; i < s.length(); i++){
c = s.charAt(i);
if(que.isEmpty() || que.peek() != c){ // notice -> que.isEmpty()
que.push(c);
}else{
que.pop();
}
}
String str ="";
while(!que.isEmpty()){
str = que.pop() + str;
// very important here, you cant say str += que.pop(). causeit will reverse the order of the output.
}
return str;
}
}

// second try -> use stack: it's actually the same with the first approach
class Solution {
public String removeDuplicates(String s) {
Stack<Character> que = new Stack<>();
for(char c : s.toCharArray()){
if(que.isEmpty() || que.peek() != c){
que.add(c);
}else{
que.pop();
}
}
String str = "";
while(!que.isEmpty()){
str = que.pop() + str ;
}
return str;
}
}


//拿字符串直接作为栈,省去了栈还要转为字符串的操作。

class Solution {
public String removeDuplicates(String s) {
// 将 res 当做栈
// 也可以用 StringBuilder 来修改字符串,速度更快
// StringBuilder res = new StringBuilder();
StringBuffer res = new StringBuffer();
// top为 res 的长度
int top = -1;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// 当 top > 0,即栈中有字符时,当前字符如果和栈中字符相等,弹出栈顶字符,同时 top--
if (top >= 0 && res.charAt(top) == c) {
res.deleteCharAt(top);
top--;
// 否则,将该字符 入栈,同时top++
} else {
res.append(c);
top++;
}
}
return res.toString();
}
}

拓展:双指针

class Solution {
public String removeDuplicates(String s) {
char[] ch = s.toCharArray();
int fast = 0;
int slow = 0;
while(fast < s.length()){
// 直接用fast指针覆盖slow指针的值
ch[slow] = ch[fast];
// 遇到前后相同值的,就跳过,即slow指针后退一步,下次循环就可以直接被覆盖掉了
if(slow > 0 && ch[slow] == ch[slow - 1]){
slow--;
}else{
slow++;
}
fast++;
}
return new String(ch,0,slow);
}
}

150. Evaluate Reverse Polish Notation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// everytime i run this version there's a bug I dont know why

class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> que = new LinkedList<>();
char c;
int res =0;
for(int i = 0; i < tokens.length; i++){
c = tokens[i].charAt(0);
if(Character.isDigit(c)){
que.push(c - '0');
}else{
int a = que.pop();
int b = que.pop();

if(c == '+'){
res = a + b;
}else if(c == '-'){
res = b - a;
}else if(c == '*'){
res = a * b;
}else {
res = b / a;
}
que.push(res);
}
}
return que.pop();
}
}

// right:

//leetcode
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new LinkedList();
for (String s : tokens) {
if ("+".equals(s)) { // leetcode 内置jdk的问题,不能使用==判断字符串是否相等
stack.push(stack.pop() + stack.pop()); // 注意 - 和/ 需要特殊处理
} else if ("-".equals(s)) {
stack.push(-stack.pop() + stack.pop()); //!!!!!!!!!!!!!!
} else if ("*".equals(s)) {
stack.push(stack.pop() * stack.pop());
} else if ("/".equals(s)) { //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!secon
int temp1 = stack.pop();
int temp2 = stack.pop();
stack.push(temp2 / temp1);
} else {
stack.push(Integer.valueOf(s));
}
}
return stack.pop();
}
}

//my + chatgpt:
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new LinkedList<>();
for (String token : tokens) {
if (isNumeric(token)) {
stack.push(Integer.parseInt(token));
} else {
int a = stack.pop();
int b = stack.pop();
int res = 0;
if(token.equals("+")){
res = a+b;
}
if(token.equals("-")){
res = b-a;
}
if(token.equals("*")){
res= a*b;
}
if(token.equals("/")){
res=b/a;
}
stack.push(res);
}
}
return stack.pop();
}

private boolean isNumeric(String str) {
try {
Integer.parseInt(str);
return true;
} catch (Exception e) {
return false;
}
}
}

second try:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// wrong version:

class Solution {
public int evalRPN(String[] tokens) {
int res = 0;
Deque<Integer> dq = new LinkedList<>();
for(String s : tokens){
if(s.equals("+")){
int num1 = dq.getLast();
int num2 = dq.getLast();
dq.removeLast();
dq.removeLast();
res = num1 + num2;
dq.addLast(res);
}else if(s.equals("-")){
int num1 = dq.getLast();
int num2 = dq.getLast();
dq.removeLast();
dq.removeLast();
res = num2 - num1;
dq.addLast(res);
}else if(s.equals("*")){
int num1 = dq.getLast();
int num2 = dq.getLast();
dq.removeLast();
dq.removeLast();
res = num2 * num1;
dq.addLast(res);
}else if(s.equals("/")){
int num1 = dq.getLast();
int num2 = dq.getLast();
dq.removeLast();
dq.removeLast();
res = num2 / num1;
dq.addLast(res);

}else{
dq.addLast(Integer.parseInt(s));
}
}
return dq.getFirst();
}
}


// right version:
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> dq = new LinkedList<>();
for (String s : tokens) {
if (s.equals("+")) {
int num1 = dq.removeLast();
int num2 = dq.removeLast();
int res = num2 + num1;
dq.addLast(res);
} else if (s.equals("-")) {
int num1 = dq.removeLast();
int num2 = dq.removeLast();
int res = num2 - num1; // Corrected the order of operands here
dq.addLast(res);
} else if (s.equals("*")) {
int num1 = dq.removeLast();
int num2 = dq.removeLast();
int res = num2 * num1;
dq.addLast(res);
} else if (s.equals("/")) {
int num1 = dq.removeLast();
int num2 = dq.removeLast();
int res = num2 / num1; // Corrected the order of operands here
dq.addLast(res);
} else {
dq.addLast(Integer.parseInt(s));
}
}
return dq.getFirst();
}
}

In both code blocks, the goal is to perform division on the two most recent elements from the Deque (num1 and num2) and then add the result back to the Deque. However, the difference lies in the order of retrieving and removing those elements from the Deque. Here’s a step-by-step explanation:

Code Block 1:

1
2
3
4
5
6
javaCopy codeint num1 = dq.getLast();  // Retrieve the last element
int num2 = dq.getLast(); // Retrieve the second-to-last element
dq.removeLast(); // Remove the last element
dq.removeLast(); // Remove the second-to-last element
res = num2 / num1; // Perform division
dq.addLast(res); // Add the result back to the Deque

Code Block 2:

1
2
3
4
javaCopy codeint num1 = dq.removeLast();  // Remove and retrieve the last element
int num2 = dq.removeLast(); // Remove and retrieve the second-to-last element
int res = num2 / num1; // Perform division
dq.addLast(res); // Add the result back to the Deque

In both cases, the division operation is the same, but here’s the key difference:

  • Code Block 1: It first retrieves the elements (num1 and num2) and then removes them from the Deque. This means that even if an exception occurred during the division (e.g., division by zero), the elements would have already been removed from the Deque, potentially altering its state.
  • Code Block 2: It directly uses removeLast() to both retrieve and remove the elements. This ensures that the elements are removed from the Deque after the division is performed. If an exception occurred during the division, the Deque would remain unchanged.

In terms of functionality, both code blocks perform the same division operation. However, Code Block 2 is safer because it preserves the integrity of the Deque even in the presence of exceptions during the division operation.

347. Top K Frequent Elements

Medium

Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any order.

Example 1:

1
2
Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// use an array to save numbers into different bucket whose index is the frequency
public class Solution {
public List<Integer> topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
for(int n: nums){
map.put(n, map.getOrDefault(n,0)+1);
}

// corner case: if there is only one number in nums, we need the bucket has index 1.
List<Integer>[] bucket = new List[nums.length+1]; //!!!! this is an array
for(int n:map.keySet()){
int freq = map.get(n);
if(bucket[freq]==null)
bucket[freq] = new LinkedList<>();
bucket[freq].add(n);
}

List<Integer> res = new LinkedList<>();
for(int i=bucket.length-1; i>0 && k>0; --i){
if(bucket[i]!=null){
List<Integer> list = bucket[i];
res.addAll(list);
k-= list.size();
}
}

return res;
}
}

List<Integer>[] is an array where each element is of type List<Integer>. Each element in the array can hold a reference to a list of integers (List<Integer>).\javaCopy code

1
2
3
4
LinkedList.addAll vs. add

LinkedList<Integer> list1 = new LinkedList<>(); list1.add(1); list1.add(2); LinkedList<Integer> list2 = new LinkedList<>(); list2.add(3); list2.add(4); list1.addAll(list2); // Merges list2 into list1

priority queue:

347.前K个高频元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

// use an array to save numbers into different bucket whose index is the frequency

// non-decreasing priority queue
public class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
for(int n: nums){
map.put(n, map.getOrDefault(n,0)+1);
}
PriorityQueue<int[]> pq = new PriorityQueue<>((pair1, pair2)->pair2[1]-pair1[1]
);
for(int key : map.keySet()){
pq.add(new int[]{key, map.get(key)}) ;
}
int[] ans = new int[k];
for(int i = 0 ; i < k; i++){
ans[i] = pq.poll()[0];
}
return ans;

}
}


// decreasing priority queue
public int[] topKFrequent2(int[] nums, int k) {
Map<Integer,Integer> map = new HashMap<>();//key为数组元素值,val为对应出现次数
for(int num:nums){
map.put(num,map.getOrDefault(num,0)+1);
}
//在优先队列中存储二元组(num,cnt),cnt表示元素值num在数组中的出现次数
//出现次数按从队头到队尾的顺序是从小到大排,出现次数最低的在队头(相当于小顶堆)
PriorityQueue<int[]> pq = new PriorityQueue<>((pair1,pair2)->pair1[1]-pair2[1]);
for(Map.Entry<Integer,Integer> entry:map.entrySet()){//小顶堆只需要维持k个元素有序
if(pq.size()<k){//小顶堆元素个数小于k个时直接加
pq.add(new int[]{entry.getKey(),entry.getValue()});
}else{
if(entry.getValue()>pq.peek()[1]){//当前元素出现次数大于小顶堆的根结点(这k个元素中出现次数最少的那个)
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;
}
}

Priority Queue

A PriorityQueue in Java is an implementation of a priority queue data structure. It is part of the Java Collections Framework and is used to store elements in a way that allows for efficient retrieval of the element with the highest (or lowest) priority based on a specified comparator or the natural ordering of the elements.

Here are key points about PriorityQueue:

  1. Priority Ordering: Elements in a PriorityQueue are stored in a way that maintains a priority order. The element with the highest priority (according to the specified comparator or natural ordering) is always at the front of the queue.

  2. Adding Elements: You can add elements to a PriorityQueue using the add or offer methods. When you add elements, they are placed in their appropriate position based on their priority.

    1
    2
    3
    4
    PriorityQueue<Integer> pq = new PriorityQueue<>();
    pq.add(3);
    pq.add(1);
    pq.add(2);

    After these operations, the pq will contain [1, 3, 2], where 1 has the highest priority.

  3. Removing Elements: You can remove the element with the highest priority using the poll method, which removes and returns the top element.

    1
    int highestPriority = pq.poll(); // Removes and returns the highest priority element (1 in this case)
  4. Peeking: You can examine the element with the highest priority without removing it using the peek method.

    1
    int highestPriority = pq.peek(); // Returns the highest priority element without removing it (1 in this case)
  5. Custom Comparators: You can define custom comparators to specify how elements are prioritized. Alternatively, if elements implement the Comparable interface, they will be ordered based on their natural ordering.

    1
    PriorityQueue<String> customPq = new PriorityQueue<>(Comparator.reverseOrder()); // Creates a descending order priority queue
  6. Performance: The PriorityQueue implementation typically provides efficient add, poll, and peek operations. These operations have logarithmic time complexity.

PriorityQueue is commonly used in various algorithms and data structures, such as Dijkstra’s algorithm for finding the shortest path in a graph, Prim’s algorithm for minimum spanning trees, and more, where elements need to be processed in a certain order based on their priorities.

In a PriorityQueue in Java, by default, the first element you remove using the poll() method is the element with the smallest priority, not necessarily the largest. The default behavior is to prioritize elements based on their natural ordering (if they implement the Comparable interface) or based on a specified comparator that you can provide when creating the PriorityQueue.

So, after you remove the first element with poll(), the next element you remove will be the one with the next smallest priority, and so on, until the queue is empty.

Here’s an example of a PriorityQueue that prioritizes elements in ascending order (smallest element first):

1
2
3
4
5
6
7
8
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.add(3);
pq.add(1);
pq.add(2);

int smallest = pq.poll(); // Removes and returns the smallest element (1)
int nextSmallest = pq.poll(); // Removes and returns the next smallest element (2)
int last = pq.poll(); // Removes and returns the last element (3)

If you want to prioritize elements in descending order (largest element first), you can create a PriorityQueue with a custom comparator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PriorityQueue<Integer> descendingPq = new PriorityQueue<>(Collections.reverseOrder());
descendingPq.add(3);
descendingPq.add(1);
descendingPq.add(2);

int largest = descendingPq.poll(); // Removes and returns the largest element (3)
int nextLargest = descendingPq.poll(); // Removes and returns the next largest element (2)
int smallest = descendingPq.poll(); // Removes and returns the smallest element (1)



import java.util.*;

PriorityQueue<Integer> descendingPq = new PriorityQueue<>((a, b) -> b - a);
descendingPq.add(3);
descendingPq.add(1);
descendingPq.add(2);

int largest = descendingPq.poll(); // Removes and returns the largest element (3)
int nextLargest = descendingPq.poll(); // Removes and returns the next largest element (2)
int smallest = descendingPq.poll(); // Removes and returns the smallest element (1)

In this case, with the Collections.reverseOrder() comparator, the PriorityQueue prioritizes elements in descending order.

java List

When it comes to using the common methods provided by the List interface, such as add, remove, get, and others, the syntax for ArrayList and LinkedList is the same. This is because both ArrayList and LinkedList are implementations of the List interface, so they share the same method signatures.

Here’s an example of some common methods with the same syntax for both ArrayList and LinkedList:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
javaCopy codeList<Integer> arrayList = new ArrayList<>();
List<Integer> linkedList = new LinkedList<>();

// Adding elements to the list
arrayList.add(1);
linkedList.add(2);

// Removing elements from the list
arrayList.remove(0);
linkedList.remove(0);

// Accessing elements by index
int element1 = arrayList.get(0);
int element2 = linkedList.get(0);





public class ListExample {
public static void main(String[] args) {
List<String> myList = new ArrayList<>();

// Adding elements
myList.add("Apple");
myList.add("Banana");
myList.add("Cherry");

// Accessing elements
String fruit = myList.get(1); // Retrieves "Banana"

// Removing elements
myList.remove(0); // Removes "Apple"

// Checking list properties
int size = myList.size(); // Returns 2
boolean isEmpty = myList.isEmpty(); // Returns false
boolean containsCherry = myList.contains("Cherry"); // Returns true

// Iterating through elements
for (String item : myList) {
System.out.println(item);
}
}
}

155. Min Stack

Medium

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.

Implement the MinStack class:

  • MinStack() initializes the stack object.
  • void push(int val) pushes the element val onto the stack.
  • void pop() removes the element on the top of the stack.
  • int top() gets the top element of the stack.
  • int getMin() retrieves the minimum element in the stack.

You must implement a solution with O(1) time complexity for each function.

Example 1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Input
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]

Output
[null,null,null,null,-3,null,0,-2]

Explanation
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); // return -3
minStack.pop();
minStack.top(); // return 0
minStack.getMin(); // return -2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class MinStack {
Stack<Integer> stack1;
Stack<Integer> min; // used to store the min value for the stack1, if you cant understand use draw it.
public MinStack() {
stack1 = new Stack<Integer>();
min = new Stack<Integer>();
}

public void push(int val) {
stack1.push(val);
if(min.isEmpty() || val <= min.peek()) min.push(val); // notice here is <= not <
}

public void pop() {
int popped = stack1.pop();
if(popped == min.peek()) min.pop();
}

public int top() {
return stack1.peek();
}

public int getMin() {
return min.peek();
}
}

/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(val);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/

678. Valid Parenthesis String

Medium

Given a string s containing only three types of characters: '(', ')' and '*', return true if s is valid.

The following rules define a valid string:

  • Any left parenthesis '(' must have a corresponding right parenthesis ')'.
  • Any right parenthesis ')' must have a corresponding left parenthesis '('.
  • Left parenthesis '(' must go before the corresponding right parenthesis ')'.
  • '*' could be treated as a single right parenthesis ')' or a single left parenthesis '(' or an empty string "".

Example 1:

1
2
Input: s = "()"
Output: true

Example 2:

1
2
Input: s = "(*)"
Output: true

Example 3:

1
2
Input: s = "(*))"
Output: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution {
public boolean checkValidString(String s) {
Stack<Integer> open = new Stack<>(); //!!! it's Stack<Integer> not character
Stack<Integer> star = new Stack<>();

for (int i = 0; i < s.length(); i++) {
if(s.charAt(i) == '(') {open.push(i);}
else if(s.charAt(i) == '*') {star.push(i);}
else {
if(!open.isEmpty()) {open.pop();} // found matched '('with')'
else if (!star.isEmpty()) {star.pop();} // match '*' with ')'
else return false; // cannot find match
}
}

// checking leftover on 2 stack
while(!open.isEmpty()) { // we can have '*' leftover, but 'open' must run out
if(star.isEmpty()) {return false;} // '(' leftover
else if (open.peek()<star.peek()){open.pop(); star.pop();} // '*'index > '(' index, matched
else {return false;} // open>top, corner case
}
return true;
}
}

387. First Unique Character in a String

Easy

Given a string s, find the first non-repeating character in it and return its index. If it does not exist, return -1.

Example 1:

1
2
Input: s = "leetcode"
Output: 0

Example 2:

1
2
Input: s = "loveleetcode"
Output: 2

Example 3:

1
2
Input: s = "aabb"
Output: -1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
Hashmap(me):
class Solution {
public int firstUniqChar(String s) {
char[] ss = s.toCharArray();
HashMap<Character, Integer> map = new HashMap<>();
for(char i : ss){
map.put(i, map.getOrDefault(i,0)+1);
}
for(int i = 0; i < ss.length; i++){
if(map.get(ss[i]) == 1){
return i;
}
}
return -1;
}
}

Queue:
class Solution {
public int firstUniqChar(String s) {
Queue<Character> ch = new LinkedList<Character>();
int len = s.length();
char temper = 0;
for(int j = 0; j < s.length(); j++)
{
ch.offer(s.charAt(j));
}
while(len > 0)
{
temper = ch.poll();
if(!ch.contains(temper)) //!!!!!
{
return s.indexOf(temper);
}
else
{
ch.offer(temper);
}
len--;
}
return -1;
}
}

1021. Remove Outermost Parentheses

Easy

A valid parentheses string is either empty "", "(" + A + ")", or A + B, where A and B are valid parentheses strings, and + represents string concatenation.

  • For example, "", "()", "(())()", and "(()(()))" are all valid parentheses strings.

A valid parentheses string s is primitive if it is nonempty, and there does not exist a way to split it into s = A + B, with A and B nonempty valid parentheses strings.

Given a valid parentheses string s, consider its primitive decomposition: s = P1 + P2 + ... + Pk, where Pi are primitive valid parentheses strings.

Return s after removing the outermost parentheses of every primitive string in the primitive decomposition of s.

Example 1:

1
2
3
4
5
Input: s = "(()())(())"
Output: "()()()"
Explanation:
The input string is "(()())(())", with primitive decomposition "(()())" + "(())".
After removing outer parentheses of each part, this is "()()" + "()" = "()()()".
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public String removeOuterParentheses(String s) {
Stack<Character> stack = new Stack<>();
char[] ss = s.toCharArray();
int start = 0;
int end = 2;
String res = "";
for(int i = 0 ; i < ss.length; i++){
if(stack.isEmpty()){
stack.add(ss[i]);
start = i+1;
}else if(ss[i] == '('){
stack.add(ss[i]);
}else{
stack.pop();
if(stack.isEmpty()){
end = i;
res += s.substring(start, end); // 之前放在括号外面所以错了
}

}
}
return res;
}
}

2232. Minimize Result by Adding Parentheses to Expression for fun

Medium

You are given a 0-indexed string expression of the form "<num1>+<num2>" where <num1> and <num2> represent positive integers.

Add a pair of parentheses to expression such that after the addition of parentheses, expression is a valid mathematical expression and evaluates to the smallest possible value. The left parenthesis must be added to the left of '+' and the right parenthesis must be added to the right of '+'.

Return expression after adding a pair of parentheses such that expression evaluates to the smallest possible value. If there are multiple answers that yield the same result, return any of them.

The input has been generated such that the original value of expression, and the value of expression after adding any pair of parentheses that meets the requirements fits within a signed 32-bit integer.

Example 1:

1
2
3
4
5
Input: expression = "247+38"
Output: "2(47+38)"
Explanation: The expression evaluates to 2 * (47 + 38) = 2 * 85 = 170.
Note that "2(4)7+38" is invalid because the right parenthesis must be to the right of the '+'.
It can be shown that 170 is the smallest possible value.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public String minimizeResult(String expression) {
String[] sp = expression.split("\\+");
String left = sp[0];
String right = sp[1];

int min = Integer.MAX_VALUE;
String result = "(" + expression + ")";

for(int i=0; i<left.length(); i++) { //Index at which we add `(` for left
int leftMul = left.substring(0, i).equals("") ? 1 : Integer.parseInt(left.substring(0,i));
int leftNum = Integer.parseInt(left.substring(i));

for(int j=1; j<=right.length(); j++) { //Index at which we add `)` for right
int rightMul = right.substring(j).equals("") ? 1 : Integer.parseInt(right.substring(j));
int rightNum = Integer.parseInt(right.substring(0,j));

int sum = leftMul * (leftNum + rightNum) * rightMul;
if(sum < min) {
min = sum;
result = left.substring(0, i) + "(" + left.substring(i) + "+" + right.substring(0, j) + ")" + right.substring(j);
}
}
}
return result;
}
}

1700. Number of Students Unable to Eat Lunch

Easy

The school cafeteria offers circular and square sandwiches at lunch break, referred to by numbers 0 and 1 respectively. All students stand in a queue. Each student either prefers square or circular sandwiches.

The number of sandwiches in the cafeteria is equal to the number of students. The sandwiches are placed in a stack. At each step:

  • If the student at the front of the queue prefers the sandwich on the top of the stack, they will take it and leave the queue.
  • Otherwise, they will leave it and go to the queue’s end.

This continues until none of the queue students want to take the top sandwich and are thus unable to eat.

You are given two integer arrays students and sandwiches where sandwiches[i] is the type of the ith sandwich in the stack (i = 0 is the top of the stack) and students[j] is the preference of the jth student in the initial queue (j = 0 is the front of the queue). Return the number of students that are unable to eat.

Example 1:

1
2
3
4
5
6
7
8
9
10
11
12
Input: students = [1,1,0,0], sandwiches = [0,1,0,1]
Output: 0
Explanation:
- Front student leaves the top sandwich and returns to the end of the line making students = [1,0,0,1].
- Front student leaves the top sandwich and returns to the end of the line making students = [0,0,1,1].
- Front student takes the top sandwich and leaves the line making students = [0,1,1] and sandwiches = [1,0,1].
- Front student leaves the top sandwich and returns to the end of the line making students = [1,1,0].
- Front student takes the top sandwich and leaves the line making students = [1,0] and sandwiches = [0,1].
- Front student leaves the top sandwich and returns to the end of the line making students = [0,1].
- Front student takes the top sandwich and leaves the line making students = [1] and sandwiches = [1].
- Front student takes the top sandwich and leaves the line making students = [] and sandwiches = [].
Hence all students are able to eat.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public int countStudents(int[] students, int[] sandwiches) {
Queue<Integer> que = new LinkedList<Integer>();

for(int i = 0; i < students.length; i++){
que.add(students[i]);
}
int i = 0;
int count = 0;
while(i < sandwiches.length && count < sandwiches.length){
if(que.size() == 0 ) return 0;
if(sandwiches[i] == que.peek()){
i++;
que.poll();
count = 0;
}else{
que.add(que.poll());
count++;
}
}

return que.size();

}
}

347. Top K Frequent Elements

Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any order.

Example 1:

1
2
Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]

Example 2:

1
2
Input: nums = [1], k = 1
Output: [1]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public int[] topKFrequent(int[] nums, int k) {
HashMap<Integer,Integer> map = new HashMap<>();
for(int i = 0 ; i < nums.length; i++){
map.put(nums[i], map.getOrDefault(nums[i],0)+1);
}
PriorityQueue<int[]> pq = new PriorityQueue<>((pair1,pair2) -> (pair2[1] - pair1[1])); //!!!
for(HashMap.Entry<Integer,Integer> entry: map.entrySet()){ //!!!
pq.add(new int[]{entry.getKey(), entry.getValue()}); //!!!
}
int[] res = new int[k];
for(int i = 0 ; i < k; i++){
res[i] = pq.poll()[0]; //!!!
}
return res;
}
}

772. Basic Calculator III

Hard

Implement a basic calculator to evaluate a simple expression string.

The expression string contains only non-negative integers, '+', '-', '*', '/' operators, and open '(' and closing parentheses ')'. The integer division should truncate toward zero.

You may assume that the given expression is always valid. All intermediate results will be in the range of [-231, 231 - 1].

Note: You are not allowed to use any built-in function which evaluates strings as mathematical expressions, such as eval().

Example 1:

1
2
Input: s = "1+1"
Output: 2

Example 2:

1
2
Input: s = "6-4/2"
Output: 4

Example 3:

1
2
Input: s = "2*(5+5*2)/3+(6/2+8)"
Output: 21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Solution {
public int calculate(String s) {
Queue<Character> queueS = new LinkedList<Character>();
for(char c : s.toCharArray()) {
queueS.add(c);
}

return helper(queueS);
}

private int helper(Queue<Character> s) {
Stack<Integer> stack = new Stack<Integer>();
char sign = '+';
int num = 0;

while (!s.isEmpty()) {
char c = s.poll();
if (Character.isDigit(c)) {
num = 10 * num + Character.getNumericValue(c);
}
// 遇到左括号开始递归计算 num
if (c == '(') {
num = helper(s);
}
// 是符号或者后面已经空了(结尾的情况)
if ((!Character.isDigit(c)) || s.isEmpty()) {
if (sign == '+') {
stack.push(num);
} else if (sign == '-') {
stack.push(-num);
} else if (sign == '*') {
stack.push(stack.pop() * num);
} else if (sign == '/') {
stack.push(stack.pop() / num);
}
num = 0;
sign = c;
}
// 遇到右括号返回递归结果
if (c == ')') {
break;
}
}
int res = 0;
for (int i : stack) {
res += i;
}
return res;
}
}