0 基础代码
Stack<Integer> st = new Stack<>(); //创建栈
st.push(); //入栈
st.pop(); //取出栈顶元素并在栈中删除
st.peek(); //返回栈顶元素
st.isEmpty(); //判断是否为空
st.size(); //统计栈内元素个数
Queue<Integer> qu = new LinkedList<>(); //构造队列
qu.offer(); //入队
qu.poll(); //出队并删除
qu.peek(); //返回队首元素
qu.isEmpty();
qu.size();
//字符串转数字
Integer.valueOf(str);
Integer.parseInt(str);
//数字转字符串
nums.toString();
String.valueOf(nums);
Map<String, String> map = new HashMap<String, String>();
map.put("key1", "value1");
map.get("key1");
map.remove("key1");
map.clear();
//遍历map
for(Map<String, String> map : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
}
1. 理论基础
栈:先进后出
队列:先进先出
2. 用栈实现队列:需要两个栈
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() {
moveStackIn();
return stackOut.pop();
}
public int peek() {
moveStackIn();
return stackOut.peek();
}
public boolean empty() {
return stackIn.isEmpty() && stackOut.isEmpty(); //只有两个都为空的时候队列才是空的
}
//将栈StackIn的元素放入到StackOut中
private void moveStackIn() {
if (!stackOut.isEmpty()) return;
while (!stackIn.isEmpty()) {
stackOut.push(stackIn.pop());
}
}
}
3. 用队列实现栈:使用一个queue实现
class MyStack {
//一个队列实现
Queue<Integer> myQueue;
public MyStack() {
myQueue = new LinkedList<>();
}
public void push(int x) {
myQueue.offer(x); //在入队的时候就处理,将最后一个之前的统统掉头
int size = myQueue.size();
while (size > 1) {
myQueue.offer(myQueue.poll()); //将最后一个之前的取出来又从队列入口放进去
size --;
}
}
public int pop() {
return myQueue.poll();
}
public int top() {
return myQueue.peek();
}
public boolean empty() {
return myQueue.isEmpty();
}
}
//用两个队列来做
class MyStack {
Queue<Integer> queue1; //它的数据顺序和栈的一样
Queue<Integer> queue2; //辅助队列,作缓存数据
public MyStack() {
queue1 = new LinkedList<>();
queue2 = new LinkedList<>();
}
public void push(int x) {
queue2.offer(x);
while (!queue1.isEmpty()) {
queue2.offer(queue1.poll());
}
Queue<Integer> queueTemp = queue1;
queue1 = queue2;
queue2 =queueTemp;
}
public int pop() {
return queue1.poll();
}
public int top() {
return queue1.peek();
}
public boolean empty() {
return queue1.isEmpty();
}
}
4 括号匹配
class Solution {
public boolean isValid(String s) {
if(s.length() % 2 != 0) return false; //长度为奇数,肯定不符合
Stack<Character> stack = new Stack<>();
for(int i = 0; i < s.length(); i++) {
int ch = s.charAt(i);
if(ch == '(') stack.push(')');
else if(ch == '[') stack.push(']');
else if(ch == '{') stack.push('}');
else if(stack.isEmpty() || stack.peek() != ch) return false;
else{
stack.pop();
}
}
return stack.isEmpty();
}
}
5. 删除字符串中所有相邻重复项
class Solution {
public String removeDuplicates(String s) {
if(s.length() == 1) {
return s;
}
int len = s.length();
Stack<Character> stack = new Stack<>();
stack.push(s.charAt(len - 1));
//从后遍历到时候直接把栈的元素一个个pop()出来就行了
for(int i = len - 2; i >= 0; i--) {
if(!stack.isEmpty()) {
if( s.charAt(i) != stack.peek()) {
stack.push(s.charAt(i));
}
else {
stack.pop();
}
}else{
stack.push(s.charAt(i)); //如果栈空了就将当前元素入栈
}
}
StringBuilder sb = new StringBuilder();
while(!stack.isEmpty()) {
sb.append(stack.pop());
}
return sb.toString();
}
}
6. 求值逆波兰表达式
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for(int i = 0; i < tokens.length; i++) {
if("+".equals(tokens[i])) {
stack.push(stack.pop() + stack.pop());
}else if("-".equals(tokens[i])){
stack.push(-stack.pop() + stack.pop());
}else if("*".equals(tokens[i])) {
stack.push(stack.pop() * stack.pop());
}else if("/".equals(tokens[i])) {
int temp1 = stack.pop();
int temp2 = stack.pop();
stack.push(temp2 / temp1);
}else {
stack.push(Integer.valueOf(tokens[i])); //转成整数
}
}
return stack.pop();
}
}
7. 滑动窗口最大值:双端队列deque
【关键在于将队列里面的元素按递减排列】
//队列求解
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
MyDeque deque = new MyDeque();
int len = nums.length;
int[] res = new int[len - k + 1];
int num = 0;
//先将前k个入队列
for(int i = 0; i < k; i++) {
deque.add(nums[i]);
}
res[num++] = deque.peek();
for(int i = k; i < nums.length; i++) {
deque.poll(nums[i - k]); //保持滑动窗口大小不变
deque.add(nums[i]); //当前元素加入队列
res[num++] = deque.peek(); //因为是单调递减的,所以直接取队首元素就好
}
return res;
}
}
class MyDeque {
Deque<Integer> deque = new LinkedList<>();
public void poll(int val) {
if(!deque.isEmpty() && val == deque.peek()) {
deque.poll();
}
}
public void add(int val) {
//如果当前元素大于队列最后一个,就将对列后面的都弹出
//保证队列是按照递减方式排列的
while(!deque.isEmpty() && val > deque.getLast()) {
deque.removeLast();
}
deque.add(val);
}
public int peek() {
return deque.peek();
}
}
8. 前k个高频元素
基于小顶堆实现,小顶堆是基于树结构实现的,它的根节点最小,大顶堆则是根节点最大。因此找前k个最小就是大顶堆,找k个最大就是小顶堆。
class Solution {
public int[] topKFrequent(int[] nums, int k) {
if(nums.length == 1) {
return nums;
}
int len = nums.length;
HashMap<Integer, Integer> map = new HashMap<>();
Arrays.sort(nums);
//统计频次,因为map里面key值能重复,所以以频次作为value值
for(int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
//System.out.println(map);
/*int freq = 1;
for(int i = 1; i < len; i++) {
if(nums[i] == nums[i - 1]) {
freq ++;
}
else{
map.put(nums[i - 1], freq);
freq = 1;
}
}
map.put( nums[len - 1], freq);
*/
//基于小顶堆实现
//小顶堆是基于树结构实现的,它的根节点最小
Queue<int[]> queue = new PriorityQueue<>((p1,p2) -> p1[1] - p2[1]);
for(Map.Entry<Integer, Integer> entry : map.entrySet()) {
if(queue.size() < k) {
queue.offer(new int[]{entry.getKey(), entry.getValue()}); //将键值对放入key中
}
else{
if(entry.getValue() > queue.peek()[1]) {
queue.poll();
queue.offer(new int[]{entry.getKey(), entry.getValue()});
}
}
}
//堆维护好了之后寻找堆里面剩余的k个最大元素
int[] res = new int[k];
for(int i = k - 1; i >= 0; i--) {
res[i] = queue.poll()[0]; //先弹出的是小的,后弹出的是大的,弹出的是key,也就是我们的nums[i]
}
return res;
}
}