队列&栈
队列 & 栈 - LeetBook - 力扣(LeetCode)全球极客挚爱的技术成长平台
设计循环队列
解法1
参考书本
class MyCircularQueue {
private int[] data;
private int head;
private int tail;
private int size;
/** Initialize your data structure here. Set the size of the queue to be k. */
public MyCircularQueue(int k) {
data = new int[k];
head = -1;
tail = -1;
size = k;
}
/** Insert an element into the circular queue. Return true if the operation is successful. */
public boolean enQueue(int value) {
if (isFull() == true) {
return false;
}
if (isEmpty() == true) {
head = 0;
}
tail = (tail + 1) % size;
data[tail] = value;
return true;
}
/** Delete an element from the circular queue. Return true if the operation is successful. */
public boolean deQueue() {
if (isEmpty() == true) {
return false;
}
if (head == tail) {
head = -1;
tail = -1;
return true;
}
head = (head + 1) % size;
return true;
}
/** Get the front item from the queue. */
public int Front() {
if (isEmpty() == true) {
return -1;
}
return data[head];
}
/** Get the last item from the queue. */
public int Rear() {
if (isEmpty() == true) {
return -1;
}
return data[tail];
}
/** Checks whether the circular queue is empty or not. */
public boolean isEmpty() {
return head == -1;
}
/** Checks whether the circular queue is full or not. */
public boolean isFull() {
return ((tail + 1) % size) == head;
}
}
/**
* 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();
*/
岛屿数量
解法1
递归
-
遍历网格
-
如果碰到1就count自加1并且进入递归:
- 按照上右下左的方向判断是否为1,如果为1再次调用自己
-
返回count计数
class Solution {
public int numIslands(char[][] grid) {
int count = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == '1') {
remove(grid, i, j);
count++;
}
}
}
return count;
}
public void remove(char[][] grid, int i, int j) {
if (i > 0 && grid[i - 1][j] == '1') {//上
grid[i - 1][j] = 0;
remove(grid, i - 1, j);
}
if (j < grid[0].length - 1 && grid[i][j + 1] == '1') {//右
grid[i][j + 1] = 0;
remove(grid, i, j + 1);
}
if (i < grid.length - 1 && grid[i + 1][j] == '1') {//下
grid[i + 1][j] = 0;
remove(grid, i + 1, j);
}
if (j > 0 && grid[i][j - 1] == '1') {//左
grid[i][j - 1] = 0;
remove(grid, i, j - 1);
}
}
}
打开转盘锁
解法1
双向bfs
-
将死亡数字放到一个set里面
-
将起始0000和最后的密码分别放到两个队列里面
-
new两个HashMap分别存放正反两个方向的数字和旋转次数的map
-
当两个队列都不为空时进行循环(如果有队列为空,那就说明找穷尽了没有找到密码,返回-1)
-
在size较小的队列里取出队头进行更新:
- 对于队头元素的每一位都有+1-1两种情况一共8种
- 如果某种情况在死亡数字里面,或者在本队列的map里,则忽略此情况
- 如果某种情况在另一个队列的map里,则返回(另一个队列map的key对应的value+本队列map的key对应的value + 1)即为最小旋转次数
- 否则继续进行循环
- 如果遍历完所有情况,没有返回,则返回-1
-
对于更新的返回值如果不为-1则返回更新的返回值
-
如果循环结束仍没有返回,则返回-1(没有解)
class Solution {
String start,end;
Set<String> dead = new HashSet<>();
public int openLock(String[] deadends, String target) {
start = "0000";
end = target;
for (String de: deadends) {
dead.add(de);
}
if (start.equals(target)) {
return 0;
}
if (dead.contains(start)) {
return -1;
}
Deque<String> queue_head = new ArrayDeque<>();
Deque<String> queue_tail = new ArrayDeque<>();
Map<String, Integer> map_head = new HashMap<>();
Map<String, Integer> map_tail = new HashMap<>();
queue_head.addLast(start);
queue_tail.addLast(end);
map_head.put(start, 0);
map_tail.put(end, 0);
while(!queue_head.isEmpty() && !queue_tail.isEmpty()) {
int result = -1;
if (queue_head.size() <= queue_tail.size()) {
result = update(queue_head, map_head, map_tail);
} else {
result = update(queue_tail, map_tail, map_head);
}
if (result != -1) {
return result;
}
}
return -1;
}
public int update(Deque<String> queue, Map<String, Integer> this_map, Map<String, Integer> other_map) {
String firstStr = queue.pollFirst();
char[] first = firstStr.toCharArray();
int step = this_map.get(firstStr);
for (int i = 0; i < 4; i++) {
for (int j = -1; j <= 1; j += 2) {
int origin = first[i] - '0';
int next = (origin + j) % 10;
if (next == -1) next = 9;
char[] clone = first.clone();
clone[i] = (char)(next + '0');
String back = String.valueOf(clone);
/*
if (first[i] == '0' && j == -1) {
first[i] = '9';
} else if (first[i] == '9' && j == 1) {
first[i] = '0';
} else {
first[i] = (char)(first[i] + j);
}
String back = String.valueOf(first);
*/
if (dead.contains(back)) {
continue;
}
if (this_map.containsKey(back)) {
continue;
}
if (other_map.containsKey(back)) {
return step + 1 + other_map.get(back);
} else {
queue.addLast(back);
this_map.put(back, step + 1);
}
}
}
return -1;
}
}
完全平方数
解法1
拉格朗日四平方和定理
拉格朗日四平方和定理说明任何一个数,都可以由小于等于4个的完全平方数相加得到。
优化:
可以采用位运算来达到更快的速度
class Solution {
public int numSquares(int n) {
int number = n;
if (two(n)) {
return 1;
}
while (n % 4 == 0) {
n = n / 4;
}
if (n % 8 == 7) {
return 4;
}
for (int i = 1; i <= (int) (Math.sqrt(number)); i++) {
if (two(number - i * i)) {
return 2;
}
}
return 3;
}
public boolean two(int n) {
int half = (int) (Math.sqrt(n));
return n == half * half;
}
}
最小栈
解法1
参考书本
class MinStack {
private Stack<StackNode> stack = new Stack<>();
//压栈
public void push(int x) {
if (empty()) {
stack.push(new StackNode(x, x));
} else {
stack.push(new StackNode(x, Math.min(x, getMin())));
}
}
//出栈
public void pop() {
if (empty())
throw new IllegalStateException("栈为空……");
stack.pop();
}
public int top() {
if (empty())
throw new IllegalStateException("栈为空……");
return stack.peek().val;
}
public int getMin() {
if (empty())
throw new IllegalStateException("栈为空……");
return stack.peek().min;
}
//判断栈是否为空
private boolean empty() {
return stack.isEmpty();
}
}
class StackNode {
public int val;
public int min;
public StackNode(int val, int min) {
this.val = val;
this.min = min;
}
}
/**
* 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();
*/
有效的括号
解法1
- 将字符串转为char数组
- 遍历数组
- 左括号进栈,右括号出栈
- 如果碰到不匹配或者该出栈的适合栈为空,返回false
- 循环结束后如果栈为空返回true,否则返回false
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
char[] ss = s.toCharArray();
for (int i = 0; i < s.length(); i++) {
if (ss[i] == '('|| ss[i] == '[' || ss[i] == '{') {
stack.push(ss[i]);
}
if (ss[i] == ')') {
if (stack.isEmpty() || stack.pop() != '('){
return false;
}
}
if (ss[i] == ']') {
if (stack.isEmpty() || stack.pop() != '['){
return false;
}
}
if (ss[i] == '}') {
if (stack.isEmpty() || stack.pop() != '{'){
return false;
}
}
}
if (stack.isEmpty()) {
return true;
}
return false;
}
}
每日温度
解法1
- new一个Stack
- 遍历数组
- 开启一个新循环,如果当前值大于栈顶的下标对应的值,给result的栈顶下标赋值为当前下标减去栈顶下标,并将栈顶出栈
- 新循环结束后,将此元素的下标入栈
- 循环结束后返回result
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
Stack<Integer> stack = new Stack<>();
int[] result = new int[temperatures.length];
for (int i = 0;i < temperatures.length; i++) {
while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
int index = stack.pop();
result[index] = i - index;
}
stack.push(i);
}
return result;
}
}
解法2
逆向遍历
- 从后往前进行遍历查找
- 对元素向后遍历,步长为已经记录的某个元素的result值(如果某个元素比当前元素小,那么那个元素的下一个更高温才有可能大于此元素)
- 如果碰到某个元素小于等于当前元素并且没有下一个更高温,则可以直接赋0
- 如果遇到了下一个更高温,则可以赋值后跳出
- 返回结果
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int[] result = new int[temperatures.length];
for (int i = temperatures.length - 1; i >= 0; i--) {
int j = i + 1;
while (j < temperatures.length) {
if (temperatures[j] > temperatures[i]) {
result[i] = j - i;
break;
} else if (result[j] == 0) {
result[i] = 0;
break;
} else {
j += result[j];
}
}
}
return result;
}
}
逆波兰表达式求值
解法1
- 遍历数组
- 如果碰到符号则取出两个栈顶数字进行运算
- 如果碰到数字则入栈
- 最后栈顶则为结果,返回栈顶
class Solution {
public int evalRPN(String[] tokens) {
List<String> list = Arrays.asList("+", "-", "*", "/");
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < tokens.length; i++) {
if (!list.contains(tokens[i])) {
stack.push(Integer.valueOf(tokens[i]));
} else {
int o2 = stack.pop();
int o1 = stack.pop();
if (tokens[i].equals("+")) {
stack.push(o1 + o2);
}
else if (tokens[i].equals("-")) {
stack.push(o1 - o2);
}
else if (tokens[i].equals("*")) {
stack.push(o1 * o2);
}
else if (tokens[i].equals("/")) {
stack.push(o1 / o2);
}
}
}
if (stack.isEmpty()) {
return 0;
}
return stack.pop();
}
}
克隆图
解法1
-
new一个HashMap来存放对应节点
-
new一个新节点
-
递归克隆neighbors
- 如果map里有节点邻居对应的key,则取出对应的value放到邻居列表中
- 如果没有,则将neighbor(key)和new一个新节点(value)放进map,然后递归克隆新节点的neighbors
-
返回新节点
/*
// Definition for a Node.
class Node {
public int val;
public List<Node> neighbors;
public Node() {
val = 0;
neighbors = new ArrayList<Node>();
}
public Node(int _val) {
val = _val;
neighbors = new ArrayList<Node>();
}
public Node(int _val, ArrayList<Node> _neighbors) {
val = _val;
neighbors = _neighbors;
}
}
*/
class Solution {
Map<Node,Node> map = new HashMap<>();
public Node cloneGraph(Node node) {
if(node == null){
return null;
}
Node node1 = new Node();
node1.val = node.val;
map.put(node, node1);
node1.neighbors = cope(node);
return node1;
}
public List<Node> cope(Node node){
List<Node> neighbors = node.neighbors;
List<Node> list = new ArrayList<>();
for (Node neighbor : neighbors) {
if (map.containsKey(neighbor)){
list.add(map.get(neighbor));
}else {
Node node1 = new Node();
node1.val = neighbor.val;
map.put(neighbor, node1);
List<Node> cope1 = cope(neighbor);
node1.neighbors = cope1;
list.add(node1);
}
}
return list;
}
}
目标和
参考文章
分享|深度讲解背包问题:面试中每五道动态规划就有一道是背包模型 ... - 力扣(LeetCode)
解法1
动态规划五部曲
解析题目意思:就是分成两堆数组left和right,其中有left-right=target,left+right=sum,联立求解有left=(traget+sum)/2。即背包0-1问题,找到一个(target+sum)/2的总和的左数组,即背包重量,数组元素是物品,权重和价值都是元素值nums[i]
1、确定dp数组以及下标的含义——这里dp[i]是指背包重量是i,子集可以凑成的该重量的所有方法数
2、确定递推公式,典型背包公式递推,dp[j] += dp[j-nums[i]]——一维,滚动数组
3、dp数组初始化,dp[0]=1,不放东西也是一种方法
4、确定遍历顺序,双层for循环遍历,i从0到numss.length(遍历所有石头),j从bagSize到当前物品的重量nums[i]
5、举例推导dp数组
6、最后返回dp[bagSize]
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum = 0;
for(int num : nums){
sum += num;
}
if(target > sum) return 0;
if((target+sum) < 0) return 0;
if((target+sum) % 2 == 1) return 0;
int bagSize = (target + sum) / 2;
int[] dp = new int[bagSize+1];
dp[0] = 1;
for(int i = 0;i < nums.length;i++){
for(int j = bagSize;j >= nums[i];j--){
dp[j] += dp[j - nums[i]];
}
}
return dp[bagSize];
}
}
二叉树的中序遍历
解法1
- 递归调用自己,参数为左节点
- 把自己加入List
- 递归调用自己,参数为右节点
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
List result = new ArrayList<Integer>();
boolean flag = false;
public List<Integer> inorderTraversal(TreeNode root) {
if (root == null) {
if (!flag) {
return result;
}
flag = true;
return null;
}
inorderTraversal(root.left);
result.add(root.val);
inorderTraversal(root.right);
return result;
}
}
解法2
- 递归
- 左根右
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
inOrder(result, root);
return result;
}
public void inOrder(List<Integer> result, TreeNode node) {
if (node == null) {
return;
}
inOrder(result, node.left);
result.add(node.val);
inOrder(result, node.right);
}
}
用栈实现队列
解法1
使用双栈
class MyQueue {
private Stack<Integer> stack1 = new Stack<>();
private Stack<Integer> stack2 = new Stack<>();
/** Initialize your data structure here. */
public MyQueue() {
}
/** Push element x to the back of queue. */
public void push(int x) {
stack1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
if(stack2.isEmpty()) {
stack1Tostack2(stack1, stack2);
}
return stack2.pop();
}
/** Get the front element. */
public int peek() {
if(stack2.isEmpty()) {
stack1Tostack2(stack1, stack2);
}
return stack2.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return stack1.isEmpty() && stack2.isEmpty();
}
private void stack1Tostack2(Stack stack1, Stack stack2) {
while(!stack1.isEmpty()) {
stack2.push(stack1.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();
*/
用队列实现栈
解法1
使用双栈
class MyStack {
Queue<Integer> queue1 = new LinkedList<>();
Queue<Integer> queue2 = new LinkedList<>();
/** Initialize your data structure here. */
public MyStack() {
}
/** Push element x onto stack. */
public void push(int x) {
queue2.offer(x);
while(!queue1.isEmpty()) {
queue2.offer(queue1.poll());
}
Queue<Integer> temp = queue1;
queue1 = queue2;
queue2 = temp;
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
return queue1.poll();
}
/** Get the top element. */
public int top() {
return queue1.peek();
}
/** Returns whether the stack is empty. */
public boolean empty() {
return queue1.isEmpty();
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/
字符串解码
解法1
双栈
- 一个栈放字符串,一个栈放数字
- 如果碰到数字,开始计数
- 碰到‘[’,将数字入栈,将目前的字符串入栈,字符串置空,数字归零
- 碰到‘]’,取出栈顶字符串,将当前字符串循环添加到栈顶字符串后,循环次数为数字栈的栈顶(出栈)
- 其他情况往字符串后面添加当前元素(用StringBuilder的append方法)
- 返回result.toString()
class Solution {
public String decodeString(String s) {
char[] st = s.toCharArray();
int number = 0;
Stack<Integer> stack_number = new Stack<>();
Stack<StringBuilder> stack_st = new Stack<>();
StringBuilder result = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (st[i] >= '0' && st[i] <= '9') {
number = number * 10 + st[i] - '0';
} else if (st[i] == '[') {
stack_st.push(result);
stack_number.push(number);
result = new StringBuilder();
number = 0;
} else if (st[i] == ']') {
StringBuilder head = stack_st.pop();
int n = stack_number.pop();
for (int j = 0; j < n; j++) {
head.append(result);
}
result = head;
} else {
result.append(st[i]);
}
}
return result.toString();
}
}
图像渲染
解法1
解法同岛屿数量那道题,可以直接看那道题
class Solution {
public int[][] floodFill(int[][] image, int sr, int sc, int color) {
if (image[sr][sc] == color) {
return image;
}
int oldColor = image[sr][sc];
image[sr][sc] = color;
color(image, sr, sc, color, oldColor);
return image;
}
public void color(int[][] grid, int i, int j, int newColor, int oldColor) {
if (i > 0 && grid[i - 1][j] == oldColor) {//上
grid[i - 1][j] = newColor;
color(grid, i - 1, j, newColor, oldColor);
}
if (j < grid[0].length - 1 && grid[i][j + 1] == oldColor) {//右
grid[i][j + 1] = newColor;
color(grid, i, j + 1, newColor, oldColor);
}
if (i < grid.length - 1 && grid[i + 1][j] == oldColor) {//下
grid[i + 1][j] = newColor;
color(grid, i + 1, j, newColor, oldColor);
}
if (j > 0 && grid[i][j - 1] == oldColor) {//左
grid[i][j - 1] = newColor;
color(grid, i, j - 1, newColor, oldColor);
}
}
}
01矩阵
解法1
- 初始化一个result数组,原数组为0的位置赋0,其他位置赋MAX_VALUE/2
- 对原数组进行两次遍历,一次从左上到右下,一次从右下到左上,填充距离矩阵
- return
class Solution {
public int[][] updateMatrix(int[][] mat) {
int[][] result = new int[mat.length][mat[0].length];
for (int i = 0; i < mat.length; i++) {
for (int j = 0; j < mat[0].length; j++) {
if (mat[i][j] == 0) {
result[i][j] = 0;
} else {
result[i][j] = Integer.MAX_VALUE / 2;
}
}
}
for (int i = 0; i < mat.length; i++) {
for (int j = 0; j < mat[0].length; j++) {
if (i - 1 >= 0) {
result[i][j] = Math.min(result[i][j], result[i - 1][j] + 1);
}
if (j - 1 >= 0) {
result[i][j] = Math.min(result[i][j], result[i][j - 1] + 1);
}
}
}
for (int i = mat.length - 1; i >= 0; i--) {
for (int j = mat[0].length - 1; j >= 0; j--) {
if (i + 1 < mat.length) {
result[i][j] = Math.min(result[i][j], result[i + 1][j] + 1);
}
if (j + 1 < mat[0].length) {
result[i][j] = Math.min(result[i][j], result[i][j + 1] + 1);
}
}
}
return result;
}
}
钥匙和房间
解法1
- 建立一个boolean的数组对应每个房间,访问过标记为true否则为flase,定义一个flag记录访问了几个房间
- 递归每个房间的钥匙,如果没访问过则将boolean数组对应值置为true,并访问该房间
- 返回flag是否等于数组长度
class Solution {
public int go = 0;
public boolean[] visited;
public boolean canVisitAllRooms(List<List<Integer>> rooms) {
visited = new boolean[rooms.size()];
gogogo(rooms, 0);
return go == visited.length;
}
public void gogogo(List<List<Integer>> rooms, int door) {
visited[door] = true;
go++;
for (int room: rooms.get(door)) {
if (!visited[room]) {
gogogo(rooms, room);
}
}
}
}