Leetcode Notes

代码随想录刷题笔记

复习

依次复习了,前中后序遍历的递归法,迭代法和统一迭代法
统一迭代法有一个简化写法, 以中序遍历为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null) return result;

Stack<TreeNode> stack = new Stack<>();
stack.push(root);

while (!stack.isEmpty()){
TreeNode node = stack.pop();
if (node != null){
if(node.right != null) stack.push(node.right);
stack.push(node);
stack.push(null);
if (node.left != null) stack.push(node.left);
} else {
result.add(stack.pop().val);
}
}

return result;
}
}

层序遍历

102 二叉树的层序遍历

https://leetcode.com/problems/binary-tree-level-order-traversal/description/

二叉树的层序遍历迭代法即BFS, 广度优先遍历

利用队列进行广度优先遍历
模拟过程
把root推入队列之中,拿出root之后,把数加入list里面,再把root的左边和右边分别推入队列,在之后,以queue的长度作为loop的依据,依次建立数组并加入result之中

具体实现

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 Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
bfs(root, result);
return result;
}
public void bfs(TreeNode root, List<List<Integer>> result){
if (root == null) return;
Queue<TreeNode> queue = new ArrayDeque<>();

queue.add(root);

while (!queue.isEmpty()){
List<Integer> itemList = new ArrayList<>();
int len = queue.size();

while (len-- > 0){
TreeNode node = queue.poll();


itemList.add(node.val);

if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
}
result.add(itemList);
}
}
}

DFS, 递归法深度优先遍历

递归法的DFS
利用深度成为索引
模拟过程
deep从0开始,把deep自加一之后,在result里面添加itemlist,再把val加入result的deep-1的list里面,之后重复左边和右边,这样在deep够深的情况是先找到深度优先,在依deep的值的大小加入相对应的list中

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 List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
dfs(root, 0, result);

return result;
}

public void dfs(TreeNode node, int depth, List<List<Integer>> result){
if (node == null) return;
depth++;

if (result.size() < depth){
List<Integer> itemList = new ArrayList<>();

result.add(itemList);
}
result.get(depth - 1).add(node.val);
if (node.left != null) dfs(node.left, depth, result);
if (node.right != null) dfs(node.right, depth, result);
}
}

107 二叉树的层序遍历II

在层序遍历的基础之上,反转结果

199 二叉树的右视图

在层序遍历的基础之上,查看是否遍历到单层最后面的元素

BFS

在判断BFS的queue的length时,不把所有的值都输入itemlist里面,只需要在length为0时的node的值输入到result里面即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution{
public List<Integer> rightSideView(TreeNode root){
List<Integer> result = new ArrayList<>();
List<List<Integer>> levelTraversal = bfs(root);
return result;
}

public List<List<Integer>> bfs(TreeNode root){
List<List<Integer>> result = new ArrayList<>();
if (root == null){
return result;
}

Queue<TreeNode> queue = new ArrayDeque<>();

queue.add(root);

while (!queue.isEmpty()){

}
}
}

DFS

先写出dfs之后,在选择所有数组中最右边的元素

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 Solution {
public List<Integer> rightSideView(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();

dfs(root, 0, result);
List<Integer> resList = new ArrayList<>();
for (int i = 0; i < result.size(); i++){
List<Integer> item = result.get(i);
resList.add(item.get(item.size() - 1));
}

return resList;
}

public void dfs(TreeNode node, int depth, List<List<Integer>> result){
if (node == null) return;
depth++;
if (result.size() < depth){
List<Integer> itemList = new ArrayList<>();

result.add(itemList);
}

result.get(depth - 1).add(node.val);

if (node.left != null) dfs(node.left, depth, result);
if (node.right != null) dfs(node.right, depth, result);
}
}

637 二叉树的层平均值

DFS或者BFS之后,计算每个itemList之中的average输出出来

BFS

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 Solution {
public List<Double> averageOfLevels(TreeNode root) {

List<Double> result = bfs(root);
return result;
}

public List<Double> bfs(TreeNode root){
List<Double> result = new ArrayList<>();
if (root == null) return result;

Queue<TreeNode> queue = new ArrayDeque<>();
queue.add(root);
while (!queue.isEmpty()){
int length = queue.size();
long sum = 0;
for (int i = 0; i < length; i++){
TreeNode node = queue.poll();
sum += node.val;
if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
}

result.add((double) sum / length);
}

return result;
}
}

DFS

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 Solution {
public List<Double> averageOfLevels(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();

dfs(root, 0, result);

List<Double> resList = new ArrayList<>();

for (int i = 0; i < result.size(); i++){
List<Integer> item = result.get(i);
long sum = 0;
for (int j : item){
sum += j;
}
resList.add((double) sum / item.size());
}

return resList;
}

public void dfs(TreeNode root, int depth, List<List<Integer>> result){
if (root == null) return;

depth++;
if (result.size()< depth){
List<Integer> itemList = new ArrayList<>();
result.add(itemList);
}

result.get(depth - 1).add(root.val);

if (root.left != null) dfs(root.left, depth, result);
if (root.right != null) dfs(root.right, depth, result);
}
}

429 N叉树树的层序遍历

BFS

遍历过程中不判断左右,而是将child都输入到queue里面

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

class Solution {
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> result = new ArrayList<>();

bfs(root, result);

return result;
}

public void bfs(Node root, List<List<Integer>> result){
if (root == null) return;
Queue<Node> queue = new ArrayDeque<>();
queue.add(root);

while(!queue.isEmpty()){
List<Integer> itemList = new ArrayList<>();
int length = queue.size();

while (length-- > 0){
Node node = queue.poll();
itemList.add(node.val);

if (node.children != null) {
for (Node i : node.children){
queue.add(i);
}
}
}
result.add(itemList);
}
}
}

DFS

同样道理

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 List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> result = new ArrayList<>();
dfs(root, 0, result);
return result;
}

public void dfs(Node root, int depth, List<List<Integer>> result){
if (root == null) return;
depth++;

if (result.size() < depth){
List<Integer> itemList = new ArrayList<>();
result.add(itemList);
}

result.get(depth - 1).add(root.val);

if (root.children != null){
for (Node i : root.children){
dfs(i,depth,result);
}
}
}
}

515 在每个树的行中找最大值

BFS或者DFS 输出的list中找到最大值

BFS

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

class Solution {
public List<Integer> largestValues(TreeNode root) {
List<Integer> result = new ArrayList<>();
bfs(root, result);
return result;
}
public void bfs(TreeNode root, List<Integer> result){
if (root == null) return;

Queue<TreeNode> queue = new ArrayDeque<>();
queue.add(root);

while (!queue.isEmpty()){
int length = queue.size();
int max = Integer.MIN_VALUE;
while (length-- > 0){
TreeNode node = queue.poll();
max = Math.max(max, node.val);

if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
}
result.add(max);

}
}
}

DFS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

class Solution {
public List<Integer> largestValues(TreeNode root) {
List<Integer> result = new ArrayList<>();

dfs(root, 0, result);
return result;
}

public void dfs(TreeNode root, int depth, List<Integer> result){
if (root == null) return;
depth++;
if (result.size() < depth){
result.add(Integer.MIN_VALUE);
}

result.set(depth - 1, Math.max(result.get(depth - 1), root.val));
if (root.left != null) dfs(root.left, depth, result);
if (root.right != null) dfs(root.right, depth, result);
}
}

116 填充每个节点的下一个右侧节点指针

BFS层序遍历记录头节点,然后连接节点

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 Node connect(Node root) {
Queue<Node> queue = new ArrayDeque<>();
if (root != null) queue.add(root);

while (!queue.isEmpty()){
int length = queue.size();
Node cur = queue.poll();
if (cur.left != null) queue.add(cur.left);
if (cur.right != null) queue.add(cur.right);

for (int i = 1; i < length; i++){
Node next = queue.poll();
if (next.left != null) queue.add(next.left);
if (next.right != null) queue.add(next.right);
cur.next = next;
cur = next;
}
}

return root;
}
}

117 填充每个节点的下一个右侧节点II

答案和116一模一样

104 二叉树的最大深度

bfs 记录层数

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

class Solution {
public int maxDepth(TreeNode root) {
return bfs(root);
}

public int bfs(TreeNode root){
if (root == null) return 0;
Queue<TreeNode> queue = new ArrayDeque<>();

queue.add(root);
int depth = 0;
while (!queue.isEmpty()){
int length = queue.size();
while (length-- > 0){
TreeNode node = queue.poll();
if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
}
depth++;
}

return depth;
}
}

111 二叉树的最小深度

利用BFS查找当左右孩子都为空时的depth,不用对比,第一个找到左右都为空的时候即为最小深度,即可返回depth

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 Solution {
public int minDepth(TreeNode root) {
return bfs(root);
}

public int bfs(TreeNode root){
if (root == null) return 0;
Queue<TreeNode> queue = new ArrayDeque<>();

queue.add(root);
int depth = 1;
while (!queue.isEmpty()){
int length = queue.size();
while (length-- > 0){
TreeNode node = queue.poll();
if (node.left == null && node.right == null){
return depth;
}
if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
}
depth++;
}

return depth;
}
}

226 翻转二叉树

递归写法: 前序和后序都可以,中序不行
具体实现

后序遍历 递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) return root;

invertTree(root.left);
invertTree(root.right);
swapChildren(root);

return root;
}

public void swapChildren(TreeNode root){
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
}
}

前序遍历 递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) return root;
swapChildren(root);
invertTree(root.left);
invertTree(root.right);

return root;
}

public void swapChildren(TreeNode root){
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
}
}

前序遍历 迭代

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 TreeNode invertTree(TreeNode root) {
if (root == null) return root;

Stack<TreeNode> stack = new Stack<>();

stack.push(root);

while (!stack.isEmpty()){
TreeNode node = stack.pop();

swapChildren(node);
if (node.right != null) stack.push(node.right);
if (node.left != null) stack.push(node.left);
}

return root;
}

public void swapChildren(TreeNode node){
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
}
}

层序遍历

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 Solution {
public TreeNode invertTree(TreeNode root) {

if(root == null) return null;
Queue<TreeNode> queue = new ArrayDeque<>();
queue.add(root);

while (!queue.isEmpty()){
int length = queue.size();
while (length-- > 0){
TreeNode node = queue.poll();

swapChildren(node);
if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
}
}

return root;

}

public void swapChildren(TreeNode node){
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
}
}

101 对称二叉树

判断二叉树是否对称,是要比较根节点的左子树和右子树是不是相互翻转的

递归法

比较左右两个子树的外侧和内侧,分别递归

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
class Solution {
public boolean isSymmetric(TreeNode root) {
return compare(root.left, root.right);
}

public boolean compare(TreeNode left, TreeNode right){
if (left == null && right != null){
return false;
}

if (left == null && right == null){
return true;
}

if (left != null && right == null){
return false;
}
if (left.val != right.val){
return false;
}

boolean compareOutside = compare(left.left, right.right);
boolean compareInside = compare(left.right, right.left);

return compareOutside && compareInside;
}
}

迭代法

利用队列,其实比较的时相同的东西

使用队列时应使用linkedlist,可以接受null pointer, 其次,应注意if的顺序先判断是否为null再判断left和right的值是否相等

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 Solution {
public boolean isSymmetric(TreeNode root) {

Queue<TreeNode> queue = new LinkedList<>();

queue.offer(root.left);
queue.offer(root.right);

while (!queue.isEmpty()){
TreeNode leftNode = queue.poll();
TreeNode rightNode = queue.poll();

if (leftNode == null && rightNode == null){
continue;
}

if (leftNode == null && rightNode != null){
return false;
}
if (leftNode != null && rightNode == null){
return false;
}

if (leftNode.val != rightNode.val){
return false;
}



queue.offer(leftNode.left);
queue.offer(rightNode.right);
queue.offer(leftNode.right);
queue.offer(rightNode.left);
}
return true;
}
}

二叉树理论基础

##二叉树的种类

  1. 满二叉树:一颗二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则二叉树为满二叉树

深度为K,有2^(k) - 1个节点的二叉树

  1. 完全二叉树,除了最底层结点可能没填满外,其余每层节点数都达到最大值,且最下面一层的结点都集中在该层最左边的若干位置

  2. 二叉搜索树,是一个有序树
    若左子树不空,则左子树上所有结点值均小于根节点的值
    若右子树不空,则右子树上所有结点值均大于根节点的值
    即他的左子树和右子树也分别为二叉搜索树

  3. 平衡二叉搜索树
    它是一颗空树,或他的左右两个子树的高度差的绝对值不超过一,并且左右两个子树都是一颗平衡二叉树

二叉树的存储方式

二叉树可以链式存储,也可以顺序存储
链式:用指针
指针存储就是,节点值和左指针,右指针的值
顺序:用数组
数组存储遍历模式,如果父节点是i,左节点即为2*i + 1,右节点即为 2 * i+ 2

二叉树的遍历方式

深度优先遍历

先往深走,遇到叶子结点再往回走
前中后的区别是中间节点的遍历顺序

  1. 前序遍历
    中左右
  2. 中序遍历
    左中右
  3. 后序遍历
    左右中


借助栈使用递归的方式实现

广度优先遍历

一层一层的遍历

层次遍历

借助队列实现一层一层遍历

二叉树的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
}
}

二叉树的递归遍历

递归三要素

  1. 确定递归函数的参数和返回值
  2. 确定终止条件
  3. 确定单层递归逻辑

144 二叉树的前序遍历

牢记前序遍历的顺序为 中 - 左 - 右

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

class Solution{
public List<Integer> preorderTraversal(TreeNode root){
List<Integer> result = new ArrayList<>();

preorder(root, result);

return result;
}

public void preorder(TreeNode root, List<Integer> result){
if (root == null) return;

result.add(root.val);
preorder(root.left, result);
preorder(root.right, result);
}
}

145 二叉树的后序遍历

后序遍历的顺序为 左 - 右 - 中左右

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();

postorder(root, result);

return result;
}

public void postorder(TreeNode root, List<Integer> result){
if (root == null) return;

postorder(root.left, result);
postorder(root.right, result);
result.add(root.val);
}
}

94 二叉树的中序遍历

中序遍历的顺序是 左 - 中 - 右指针的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();

inorder(root, result);

return result;
}

public void inorder(TreeNode root, List<Integer> result){
if (root == null) return;

inorder(root.left, result);
result.add(root.val);
inorder(root.right, result);
}
}


二叉树的迭代遍历

递归的实现就是:每一次递归调用都会把函数的局部变量,参数值和返回地址压入调用栈中

由此 前中后序遍历也可使用栈进行遍历

前序遍历

前序遍历是中 - 左 - 右

现将根节点放入栈中,然后把右孩子加入栈,再讲左孩子加入栈

现将root节点放入,之后把root节点值输出之后,先判断右边,再判断左边,之后循环这一个过程,直到栈空

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

class Solution{
public List<Integer> preorderTraversal(TreeNode root){
List<Integer> result = new ArrayList<>();

if (root == null){
return result;
}

Stack<TreeNode> stack = new Stack<>();
stack.push(root);

while (!stack.isEmpty()){
TreeNode node = stack.pop();
result.add(node.val);

if (node.right != null){
stack.push(node.right);
}

if (node.left != null){
stack.push(node.left);
}
}

return result;
}
}

中序遍历

使用迭代法写中序遍历时,用指针遍历访问节点,用栈处理节点上的元素

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


class Solution{
public List<Integer> inorderTraversal(TreeNode root){
List<Integer> result = new ArrayList<>();

if (root == null) return result;

Stack<TreeNode> stack = new Stack<>();

TreeNode cur = root;

while (cur != null || !stack.isEmpty()){
if (cur != null){
stack.push(cur);
cur = cur.left;
} else {
cur = stack.pop();
result.add(cur.val);

cur = cur.right;
}
}

return result;
}
}

后序遍历

前序遍历先把左右调换顺序之后把result数组反过来就可以成为后序遍历的顺序

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 List<Integer> postorderTraversal(TreeNode root){
List<Integer> result = new ArrayList<>();


if (root == null) return result;

Stack<TreeNode> stack = new Stack<>();

stack.push(root);

while (!stack.isEmpty()){
TreeNode node = stack.pop();

result.add(node.val);

if (node.left != null) stack.push(node.left);

if (node.right != null) stack.push(node.right);
}

Collections.reverse(result);

return result;
}
}

二叉树的统一迭代

以中序遍历为例,无法同时解决访问节点和处理节点不一致的情况

此时,把访问节点放入栈中,把要处理的结点也放入栈中在处理节点之后放入空指针作为标记

即读到null后,跳两格读数,因为顺序在之前已经写好了,只需要处理读数问题

前序遍历

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

class Solution{
public List<Integer> preorderTraversal(TreeNode root){
List<Integer> result = new ArrayList<>();

if (root == null) return result;

Stack<TreeNode> stack = new Stack<>();

stack.push(root);

while (!stack.isEmpty()){
TreeNode node = stack.peek();

if (node != null){
stack.pop();
if (node.right != null) stack.push(node.right);
if (node.left != null) stack.push(node.left);
stack.push(node);
stack.push(null);
} else {
stack.pop();
node = stack.pop();
result.add(node.val);
}
}

return result;
}
}

中序遍历

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 Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();

if (root == null) return result;

Stack<TreeNode> stack = new Stack<>();

stack.push(root);

while (!stack.isEmpty()){
TreeNode node = stack.peek();
if (node != null){
stack.pop();
if (node.right != null) stack.push(node.right);
stack.push(node);
stack.push(null);
if (node.left != null) stack.push(node.left);
} else {
stack.pop();
node = stack.peek();
stack.pop();
result.add(node.val);
}

}

return result;
}
}

后序遍历

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

class Solution{
public List<Integer> postorderTraversal(TreeNode root){
List<Integer> result = new ArrayList<>();

if (root == null) return result;
Stack<TreeNode> stack = new Stack<>();

stack.push(root);

while (!stack.isEmpty()){
TreeNode node = stack.peek();

if (node != null){
stack.pop();

stack.push(node);
stack.push(null);
if (node.right != null) stack.push(node.right);
if (node.left != null) stack.push(node.left);
} else {
stack.pop();
result.add(stack.pop().val);
}
}

return result;

}
}

239 滑动窗口最大值

https://leetcode.com/problems/sliding-window-maximum/description/
在线性时间复杂度完成

经典使用单调队列的题目

难点是如何求一个区间里面的最大值

此时需要一个队列,放进窗口元素,然后随着窗口的移动,队列一进一出,每次移动之后,队列返回最大值

此队列没有必要维护窗口里的所有元素,只需要维护可能成为窗口里最大值的元素就可以,同时保证队列里的元素数值是由大到小的
维护元素单调递减或递增(这里是递减)的队列就是单调队列

如何在不维护所有元素的情况下配合窗口移动

pop() 如果窗口移除的元素value等于单调队列的出口元素,队列弹出元素,否则不用操作
push() 如果push的元素大于入口元素的数值,将队列入口弹出,指到push元素的数值小于等于队列入口元素数值为止

保持这个规则,que.front()就一直会是最大值

  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
    44
    45
    46
    47
    48
    49
    50

    class MyQueue{
    Deque<Integer> myqueue = new LinkedList<>();

    // 如果队列顶不是要退出的值,不动
    void poll(int val){
    if (!myqueue.isEmpty() && val == myqueue.peek()){
    myqueue.pollFirst();
    }
    }
    // 推入的值和之前的值比较,推入的值大,就删一个,一直删到没有或者小于原队列的值
    void add(int val){
    while (!myqueue.isEmpty() && val > myqueue.getLast()){
    myqueue.removeLast();
    }

    myqueue.add(val);
    }
    // 队列顶部一定是最大值
    int peek(){
    return myqueue.peek();
    }
    }

    class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
    if (nums.length == 1){
    return nums;
    }

    MyQueue record = new MyQueue();
    int[] result = new int[nums.length - k + 1];

    for (int i = 0; i < k; i++){
    record.add(nums[i]);
    }
    result[0] = record.peek();

    int index = 1;
    for (int i = k; i < nums.length; i++){
    record.poll(nums[i-k]);
    record.add(nums[i]);
    result[index++] = record.peek();
    }

    return result;
    }
    }


  2. 利用双端队列手动实现单调队列
    用一个单调队列存储对应下标,窗口滑动时,直接取队列的头部指针对应的值放入结果集中

因为单调队列存储的时下标,则需要满足两个条件
1. 队列的头节点需要在[i - k + 1, i]之中
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

class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
ArrayDeque<Integer> record = new ArrayDeque<>();

int[] result = new int[nums.length - k + 1];
int index = 0;

for (int i = 0; i < nums.length; i++){
while (!record.isEmpty() && record.peek() < i - k + 1){
record.poll();
}

while (!record.isEmpty() && nums[record.getLast()] < nums[i]){
record.removeLast();
}

record.add(i);

if (i >= k - 1){
result[index++] = nums[record.peek()];
}
}

return result;
}
}

前K个高频元素

https://leetcode.com/problems/top-k-frequent-elements/description/

此题分三步

  1. 统计元素出现频率
  2. 对频率排序
  3. 找出前K个高频元素

统计用map来做

终点是排序即优先级队列
优先级队列对外接口只是从队头取元素,从队尾添加元素
优先级队列内部元素是自动依照元素权值排列
堆是一颗完全二叉树,树中每个节点的值都不小于或不大于左右孩子的值
如果父亲节点大于等于左右孩子就是大顶堆
如果父亲节点小于等于左右孩子就是小顶堆

此题使用优先级队列对部分频率进行排序
因为要统计最大前K个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里累积的才是前K个最大元素

具体实现思路
先用map统计频率,之后构建小顶堆,将所有频率入到堆中,如果堆大小大于K,弹出堆顶,之后倒叙构建数组

  1. 小顶堆实现

注意代码写法
统计次数, map的getOrDefault写法

怎么利用PriorityQueue建立小顶堆

添加到小顶堆的操作

顶堆的poll()
add()

遍历map
Map.Entry<Integer,Integer> entry : map.entrySet()

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
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> record = new HashMap<>();

for (int i : nums){
record.put(i, record.getOrDefault(i, 0) + 1);
}

PriorityQueue<int[]> pq = new PriorityQueue<>((pair1, pair2) -> pair1[1] - pair2[1]);

for (Map.Entry<Integer, Integer> entry: record.entrySet()){
if (pq.size() < k){
pq.add(new int[] {entry.getKey(), entry.getValue()});
} else {
if (pq.peek()[1] < entry.getValue()){
pq.poll();
pq.add(new int[] {entry.getKey(), entry.getValue()});
}
}
}

int[] result = new int[k];
for (int i = k - 1; i >= 0; i--){
result[i] = pq.poll()[0];
}

return result;
}
}
  1. 大顶堆实现
    大顶堆实现,比较暴力
    把所有元素全部推入大顶堆中,有K个元素,就poll()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
class Solution {
public int[] topKFrequent(int[] nums, int k) {

Map<Integer,Integer> record = new HashMap<>();

for (int i : nums){
record.put(i, record.getOrDefault(i, 0) + 1);
}

PriorityQueue<int[]> pq = new PriorityQueue<>((pair1, pair2) -> pair2[1] - pair1[1]);

for (Map.Entry<Integer, Integer> entry : record.entrySet()){
pq.add(new int[] {entry.getKey(), entry.getValue()});
}

int[] result = new int[k];
for (int i = 0; i < k; i++){
result[i] = pq.poll()[0];
}

return result;

}
}


洗盘子

注意stack的用法

.peek()
.push()
.isEmpty()
.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
28
29
30
31
32
33
34
35
import java.util.*;

public class Main{

public static void main(String[] args){
Scanner sc = new Scanner(System.in);

Stack<Integer> record = new Stack<>();

while (sc.hasNextInt()){
int n = sc.nextInt();

while (n-- > 0){
record.push(sc.nextInt());
}

int m = sc.nextInt();
while (m-- > 0){
int x = sc.nextInt();
if (x == 1){
if(!record.isEmpty()) record.pop();
} else if (x == 2){
record.push(sc.nextInt());

}
}

if (record.isEmpty()){
System.out.println("All the dishes have been washed.");
} else {
System.out.println(record.peek());
}
}
}
}

排队去奶茶

ArrayDeque
.add();
.poll();
.peek();
.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
import java.util.*;

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
Deque<String> record = new ArrayDeque<>();

int n = sc.nextInt();
sc.nextLine();

String[] names = sc.nextLine().split(" ");

for (int i = 0; i < n; i++){
record.addLast(names[i]);
}

int m = sc.nextInt();
while (m-- > 0){
int step = sc.nextInt();
if (step == 1){
if (!record.isEmpty()) record.pollFirst();
} else {
record.offer(sc.next());
}
}

if (record.isEmpty()) {
System.out.println("There are no more people in the queue.");
} else {
System.out.println(record.peek());
}
}
}

图形的面积

class整体用法

封装,尽量把class里面的属性封装成private,利用getter和setter函数读取和修正
继承,用extends继承父类,语法为子类extends父类
抽象类,当创建实例没有意义时,把class抽象掉,再把方法抽象掉(只能在抽象class里面)【只规定return type和方法名字】
多态,Shape建立后,可以有不同的子类,子类可以调用不同子类之下的函数

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
import java.util.*;

abstract class Shape{
protected String type;

public abstract double CalculateArea();

public String getType(){return this.type;}
}

class Rectangle extends Shape{
private int width;
private int height;

public Rectangle(int width, int height){
this.type = "Rectangle";
this.width = width;
this.height = height;
}

public double CalculateArea(){
return (double) (width * height);
}
}

class Circle extends Shape{
private int radius;

public Circle(int radius){
this.type = "Circle";
this.radius = radius;
}

public double CalculateArea(){
return 3.14 * radius * radius;
}
}

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while (sc.hasNext()){
String type = sc.next();

if (type.equals("rectangle")){
Shape rec = new Rectangle(sc.nextInt(), sc.nextInt());
System.out.printf("Rectangle area: %.2f%n", rec.CalculateArea());
} else if (type.equals("circle")){
Shape cir = new Circle(sc.nextInt());
System.out.printf("Circle area: %.2f%n", cir.CalculateArea());
} else{
break;
}
}
}
}

A + B 问题IV

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.*;

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);

while (sc.hasNextInt()){
int n = sc.nextInt();
if (n != 0){
int sum = 0;
for (int i = 0; i < n; i++){
sum += sc.nextInt();
}

System.out.println(sum);
} else {
break;
}

}

}
}

A + B 问题VII

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.*;

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);

while (sc.hasNextInt()){
int a = sc.nextInt();
int b = sc.nextInt();
System.out.println(a + b);
System.out.println();
}
}
}

运营商活动

result 从 m开始

注意while loop 的判断条件,m和n的关系

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
import java.util.*;

public class Main{

public static void main(String[] args){
Scanner sc = new Scanner(System.in);

while (sc.hasNextInt()){
int m = sc.nextInt();
int n = sc.nextInt();

if (m == 0 && n == 0){
break;
} else {
System.out.println(getMaximumDays(m, n));
}
}
}

public static int getMaximumDays(int m, int n){
int result = m;
while (m >= n){
int step = m / n;
result += step;
m = m - (step * n) + step;
}
return result;
}
}

共同祖先

利用HashMap 溯源到同一个位置,数各自的数量

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
import java.util.*;

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);

while (sc.hasNextInt()){
int n = sc.nextInt();
Map<Integer, Integer> record = new HashMap<>();
while (n-- > 0){
int a = sc.nextInt();
int b = sc.nextInt();

record.put(a, b);
}
getResult(record);


}
}

public static void getResult(Map<Integer, Integer> record){
int count_ming = 0;
int count_yu = 0;

int yu = 1;
int ming = 2;

while (record.containsKey(yu)){
count_yu++;
yu = record.get(yu);
}

while (record.containsKey(ming)){
count_ming++;
ming = record.get(ming);
}

if (count_yu > count_ming){
System.out.println("You are my elder");
} else if (count_yu < count_ming){
System.out.println("You are my younger");
} else {
System.out.println("You are my brother");
}
}
}

链表的基础操作

链表的基础操作I

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
import java.util.*;

class ListNode{
int val;
ListNode next;

ListNode(){}
ListNode(int val) {
this.val = val;
}
}


class LinkList{

int size;
ListNode head;

public LinkList(){
this.head = new ListNode(0);
}

public void insert(int val){
ListNode newNode = new ListNode(val);

ListNode pre = this.head;
int index = size;
while (index-- > 0){
pre = pre.next;
}
pre.next = newNode;
this.size++;
}

public void printList(){
ListNode cur = head.next;

while (cur != null){
System.out.print(cur.val + " ");
cur = cur.next;
}
System.out.println();
}

}

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while (sc.hasNextInt()){
int n = sc.nextInt();
LinkList result = new LinkList();
while (n-- > 0){
int m = sc.nextInt();
result.insert(m);
}
result.printList();
}
sc.close();
}

}

链表的基础操作II

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


import java.util.*;

class ListNode{
int val;
ListNode next;
ListNode(){}
ListNode(int val){this.val = val;}
}

class LinkedList {
int size;
ListNode head;

LinkedList(){
this.size = 0;
this.head = new ListNode(0);
}

public int get(int index){
if (index < 1 || index > size){
System.out.println("Output position out of bounds.");
return -1;
}

ListNode cur = head.next;

for(int i = 1; i < index; i++){
cur = cur.next;
}

System.out.println(cur.val);
return cur.val;
}
public void insert(int val){
ListNode pre = head;
ListNode newNode = new ListNode(val);
for (int i = 0; i < size; i++){
pre = pre.next;
}

pre.next = newNode;
this.size++;
}

public void printList(){
ListNode cur = head.next;
while (cur != null){
System.out.print(cur + " ");
cur = cur.next;
}

System.out.println();
}
}

class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);

while(sc.hasNextInt()){
int n = sc.nextInt();
int k = sc.nextInt();
LinkedList result = new LinkedList();
while (n-- > 0){
result.insert(sc.nextInt());
}
while (k-- > 0){
result.get(sc.nextInt());
}
}

sc.close();
}
}

链表的基础操作III

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
95
96
97
98
99
100
101
102
103
104
import java.util.*;


class ListNode{

int val;
ListNode next;
ListNode(){}
ListNode(int val){
this.val = val;
}
}

class LinkedList{
ListNode head;
int size;

LinkedList(){
this.size = 0;
this.head = new ListNode(0);
}

public boolean insert(int index, int val){
if (index < 1 || index > size + 1){

return false;
}
this.size++;
ListNode newNode = new ListNode(val);

ListNode pre = head;

for(int i = 0; i < index - 1; i++){
pre = pre.next;
}

newNode.next = pre.next;
pre.next = newNode;

return true;
}

public boolean delete(int index){
if (index < 1 || index > size){
return false;
}
this.size--;
ListNode pre = head;

for(int i = 0; i < index - 1; i++){
pre = pre.next;
}

pre.next = pre.next.next;
return true;
}

public void printList(){
ListNode cur = head.next;
while (cur != null){
System.out.print(cur.val + " ");
cur = cur.next;
}
System.out.println();
}
}

class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while(sc.hasNextInt()){
int k = sc.nextInt();
LinkedList result = new LinkedList();
while (k-- > 0){
result.insert(result.size + 1, sc.nextInt());
}

int S = sc.nextInt();
while (S-- > 0){
int n = sc.nextInt();
int x = sc.nextInt();

if (result.insert(n,x)){
result.printList();
} else {
System.out.println("Insertion position is invalid.");
}
}

int L = sc.nextInt();
while (L-- > 0){
int m = sc.nextInt();
if (result.delete(m)){
result.printList();
} else {
System.out.println("Deletion position is invalid.");
}
}
}

sc.close();
}
}

出现频率最高的字母

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
import java.util.*;

class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);

while (sc.hasNextInt()){
int n = sc.nextInt();
sc.nextLine();

while (n-- > 0){
int[] record = new int[26];
String s = sc.nextLine();
char[] str = s.toCharArray();
for (char i : str){
record[i - 'a']++;
}
int result = -1;
int getIndex = 0;
for (int i = 0; i < record.length; i++){
if (record[i] > result){
result = record[i];
getIndex = i;
}
}

System.out.println((char)(getIndex + 'a'));
}
}
}
}

判断集合成员

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
import java.util.*;

class Main{
public static void main(String[] args){

Scanner sc = new Scanner(System.in);
while (sc.hasNextInt()){
int k = sc.nextInt();

while (k-- > 0){
int m = sc.nextInt();
Set<Integer> record = new HashSet<>();
while (m-- > 0){
record.add(sc.nextInt());
}

int n = sc.nextInt();
if (record.contains(n)){
System.out.println("YES");
} else {
System.out.println("NO");
}
}
}
}
}

开房门

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
import java.util.*;

class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);

while (sc.hasNextInt()){
int s = sc.nextInt();
while (s-- > 0){
int n = sc.nextInt();

Map<Integer, Integer> record = new HashMap<>();

while (n-- > 0){
int k = sc.nextInt();
int d = sc.nextInt();

record.put(d,k);
}

int x = sc.nextInt();
if (record.containsKey(x)){
System.out.println(record.get(x));
} else {
System.out.println("Can't open the door.");
}
}
}
}
}

20 有效括号

https://leetcode.com/problems/valid-parentheses/description/

经典栈问题,把左括号转化后填进栈中
如果不是左括号,看栈为空不为空,栈是空的或栈顶不是对应的右括号 false
都不是就推出栈顶,最后看栈为不为空

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 isValid(String s){
Stack<Character> checkP = new Stack<>();

for (int i = 0; i < s.length(); i++){
char stream = s.charAt(i);

if (stream == '('){
checkP.push(')');
} else if (stream == '{'){
checkP.push('}');
} else if (stream == '['){
checkP.push(']');
} else if (checkP.isEmpty() || checkP.peek() != stream){
return false;
} else {
checkP.pop();
}
}

return checkP.isEmpty();
}
}

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

https://leetcode.com/problems/remove-all-adjacent-duplicates-in-string/submissions/1137766541/
栈的另一经典问题

查看字符串中的栈顶元素,如果为空或者不是当前stream元素,push进去
是当前stream元素,把栈顶pop出来

因为当前栈是倒序所以把顺序反过来打印

第一种 用栈解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution{
public String removeDuplicates(String s){
Deque<Character> result = new ArrayDeque<>();

for (int i = 0; i < s.length(); i++){
char stream = s.charAt(i);

if (result.isEmpty() || result.peek() != stream){
result.push(stream);
} else {
result.pop();
}
}

String str = "";

while (!result.isEmpty()){
str = result.pop() + str;
}

return str;
}
}

第二种用字符串直接作为栈
利用StringBuilder 检查stream里面的值,原理和栈相同,不用倒序插入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution{
public String removeDuplicates(String s){
StringBuilder sb = new StringBuilder();

for (int i = 0; i < s.length(); i++){
if (sb.length() == 0 || sb.charAt(sb.length() - 1) != s.charAt(i)){
sb.append(s.charAt(i));
} else {
sb.deleteCharAt(sb.length() - 1);
}
}

return sb.toString();
}
}

第三种 双指针
把快指针的值覆盖到慢指针处
比较慢指针位置和慢指针前一位置,相同慢指针减一,不同慢指针加一
快指针一直遍历整个数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution{
public String removeDuplicates(String s){
int left = 0;
int right = 0;
char[] result = s.toCharArray();
while (right < s.length()){
result[left] = result[right];
if (left > 0 && result[left] == result[left - 1]){
left--;
} else {
left++;
}

right++;
}

return new String(result, 0, left);
}
}

150 逆波兰表达式求值

逆波兰表达式是一种后缀表达式,所谓后缀是指运算符写在后面
用栈来实现,读到’+’, ‘-‘ , ‘*’ , ‘/‘ 后推出栈顶两个元素进行运算,之后把结果推回栈顶
https://leetcode.com/problems/evaluate-reverse-polish-notation/description/

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 int evalRPN(String[] tokens){
Deque<Integer> result = new ArrayDeque<>();

for (String i : tokens){
if (i.equals("+")){
result.push(result.pop() + result.pop());
} else if (i.equals("-")){
result.push(-result.pop() + result.pop());
} else if (i.equals("*")){
result.push(result.pop() * result.pop());
} else if (i.equals("/")){
int divisor = result.pop();
int dividend = result.pop();
result.push(dividend / divisor);
} else {
result.push(Integer.valueOf(i));
}
}

return result.pop();
}
}

844 比较含退格的字符串

实时变换字符串,注意退格符超过string长度的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public boolean backspaceCompare(String s, String t) {
String sB = getFinalString(s);
String tB = getFinalString(t);
return sB.equals(tB);
}

private String getFinalString(String s){
StringBuilder sb = new StringBuilder();

for (int i = 0; i < s.length(); i++){
if (s.charAt(i) != '#'){
sb.append(s.charAt(i));
} else {
sb.delete(sb.length() == 0 ? 0 :sb.length() - 1, sb.length());
}
}

return sb.toString();
}
}

栈与队列理论基础

队列,先进先出
栈, 先进后出

栈,push(), pop(),top();

注意deque是双向队列,封住一端,可以成为栈

232 用栈实现队列

push(x) 放入队列尾部
pop() 从队列首部移除元素
peek() 返回队列首部元素
empty() 返回队列是否为空

Java 创建栈的方法 Stack
栈的使用方法
.push(x) 放入栈
.pop() 退出栈
.peek() 返回栈顶元素
.isEmpty() 栈是否为空

用栈实现队列,需要两个栈,一个栈进,一个栈出
如果队列要pop就要把栈进元素推进栈出,然后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
28
29
30
31
32
33
34
35
36
37
38
39
40

class MyQueue {
Stack<Integer> stackIn;
Stack<Integer> stackOut;

public MyQueue() {
this.stackIn = new Stack<>();
this.stackOut = new Stack<>();
}

public void push(int x) {
stackIn.push(x);

}

public int pop() {
dumpInStackOut();
return stackOut.pop();

}

public int peek() {
dumpInStackOut();
return stackOut.peek();

}

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

}

private void dumpInStackOut(){
if (!stackOut.isEmpty()) return;
while (!stackIn.isEmpty()){
stackOut.push(stackIn.pop());
}
}
}

225 用队列实现栈

push(x) 元素x入栈
pop() 移除栈顶元素
top() 获取栈顶元素
empty() 返回栈是否为空

两个队列实现
用两个队列que1和que2实现队列的功能,que2其实完全就是一个备份的作用,把que1最后面的元素以外的元素都备份到que2,然后弹出最后面的元素,再把其他元素从que2导回que1。

先把新元素推入q2之后把q1推到q2,之后q1 q2交换

LinkedList Queue用法
.offer(x) 尾部添加元素
.isEmpty() 队列是否为空
.poll() 头部推出元素
.peek() 返回头部元素

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
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();
}
}

两个ArrayDeque 但是实际是两个Queue
ArrayDeque queue 用法
.add(int x) 尾部添加元素
.poll() 推出头部元素
.peek() 返回头部元素
.isEmpty() 队列是否为空

先把q1 推到q2 之后q1推入新元素,再之后把q2推入q1完成队列

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

class MyStack {
Queue<Integer> queue1;
Queue<Integer> queue2;

public MyStack(){
queue1 = new ArrayDeque<>();
queue2 = new ArrayDeque<>();
}

public void push(int x){
while (!queue1.isEmpty()){
queue2.add(queue1.poll());
}

queue1.add(x);

while (!queue2.isEmpty()){
queue1.add(queue2.poll());
}

}

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

public int top(){ return queue1.peek();}
public boolean empty() {return queue1.isEmpty();}
}

两个Deque实现
.addLast();
.pollFirst();
.peekFirst();

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
class MyStack {
// Deque 接口继承了 Queue 接口
// 所以 Queue 中的 add、poll、peek等效于 Deque 中的 addLast、pollFirst、peekFirst
Deque<Integer> que1; // 和栈中保持一样元素的队列
Deque<Integer> que2; // 辅助队列
/** Initialize your data structure here. */
public MyStack() {
que1 = new ArrayDeque<>();
que2 = new ArrayDeque<>();
}

/** Push element x onto stack. */
public void push(int x) {
que1.addLast(x);
}

/** Removes the element on top of the stack and returns that element. */
public int pop() {
int size = que1.size();
size--;
// 将 que1 导入 que2 ,但留下最后一个值
while (size-- > 0) {
que2.addLast(que1.peekFirst());
que1.pollFirst();
}

int res = que1.pollFirst();
// 将 que2 对象的引用赋给了 que1 ,此时 que1,que2 指向同一个队列
que1 = que2;
// 如果直接操作 que2,que1 也会受到影响,所以为 que2 分配一个新的空间
que2 = new ArrayDeque<>();
return res;
}

/** Get the top element. */
public int top() {
return que1.peekLast();
}

/** Returns whether the stack is empty. */
public boolean empty() {
return que1.isEmpty();
}
}


一个Deque实现
调整q1顺序,把最后的元素推出

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
class MyStack {
// Deque 接口继承了 Queue 接口
// 所以 Queue 中的 add、poll、peek等效于 Deque 中的 addLast、pollFirst、peekFirst
Deque<Integer> que1; // 和栈中保持一样元素的队列
Deque<Integer> que2; // 辅助队列
/** Initialize your data structure here. */
public MyStack() {
que1 = new ArrayDeque<>();
}

/** Push element x onto stack. */
public void push(int x) {
que1.addLast(x);
}

/** Removes the element on top of the stack and returns that element. */
public int pop() {
int size = que1.size();
size--;
// 将 que1 导入 que2 ,但留下最后一个值
while (size-- > 0) {
que1.addLast(que1.peekFirst());
que1.pollFirst();
}

int res = que1.pollFirst();
return res;
}

/** Get the top element. */
public int top() {
return que1.peekLast();
}

/** Returns whether the stack is empty. */
public boolean empty() {
return que1.isEmpty();
}
}

一个队列实现
一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时再去弹出元素就是栈的顺序了。

把新元素推入queue之后,重新排列,把新元素推到顶上

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
class MyStack{
Queue<Integer> queue;

pulic MyStack(){
queue = new LinkedList<>();
}

public void push(int x){
queue.offer(x);
int size = queue.size();
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();
}
}

#A + B 问题

##A + B 问题 I
创建class, 创建main函数, Scanner用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while (sc.hasNextInt()){
int a = sc.nextInt();
int b = sc.nextInt();

System.out.println(a + b);
}

sc.close();
}
}

##A + B 问题 II
for循环, while循环, ++ 和 –;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

import java.util.*;

class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while(sc.hasNextInt()){
int n = sc.nextInt();

//while(n-- >0)
for (int i = 0; i < n ;i++){
int a = sc.nextInt();
int b = sc.nextInt();

System.out.println(a + b);
}
}

sc.close();
}
}

##A + B 问题 III
if 语句,关系运算,break退出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.util.*;
class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while (sc.hasNextInt()){
int a = sc.nextInt();
int b = sc.nextInt();

if (a == 0 && b == 0){
break;
} else {
System.out.println(a + b);
}
}

sc.close();
}
}

##A + B 问题 IV
累加运算,算术运算, 赋值运算,三元运算

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
import java.util.*;

public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNextInt()){
int n = sc.nextInt();
if (n == 0){
break;
}

int sum = 0;
while (n-- > 0){
sum += sc.nextInt();
}

System.out.println(sum);
}


sc.close();

}

}

##A + B 问题 VIII

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.util.*;

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);

while (sc.hasNextInt()){
int n = sc.nextInt();
sc.nextLine();
while (n-- > 0){
int m = sc.nextInt();
int sum = 0;
while (m-- > 0){
sum += sc.nextInt();
}

System.out.println(sum);
if (n != 0) System.out.println();
}
}
}

}

#倒序输出数组与隔位输出

##fix length 数组

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
import java.util.*;

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);

while (sc.hasNextInt()){
int n = sc.nextInt();
int[] record = new int[n];

for (int i = 0; i < n; i++){
record[i] = sc.nextInt();
}

//倒序输出

for(int i = n - 1; i >= 0; i--){
System.out.print(record[i] + " ");
}

System.out.println();

//隔位输出
for (int i = 0; i < n; i += 2){
System.out.print(record[i] + " ");
}
}

sc.close();
}

}

##ArrayList 实现
ArrayList 方法
添加:.add()
查找:.get(index)
长度:.size()
删除:.remove(index)

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
import java.util.*;

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);

while (sc.hasNextInt()){
int n = sc.nextInt();
List<Integer> record = new ArrayList<>();

for (int i = 0; i < n; i++){
record.add(sc.nextInt());
}
//倒序输出

for(int i = record.size() - 1; i >= 0; i--){
System.out.print(record.get(i) + " ");
}

System.out.println();

//隔位输出
for (int i = 0; i < record.size(); i += 2){
System.out.print(record.get(i) + " ");
}
}

sc.close();
}

}

摆平积木

算average的方法

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
import java.util.*;

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);


while (sc.hasNextInt()){
int n = sc.nextInt();

if (n == 0){
break;
}
List<Integer> record = new ArrayList<>();
int sum = 0;
int m = n;
while (m-- > 0){
int temp = sc.nextInt();
record.add(temp);
sum += temp;
}

int recordAvg = sum / n;
int result = 0;
for (int i = 0; i < n; i++){
if (record.get(i) > recordAvg){
result += record.get(i) - recordAvg;
}
}
System.out.println(result);
System.out.println();
}


}
}

奇怪的信

找到int中每个数字的做法
n % 10 找到数字
n /= 10 除去数字
n % 2 判断奇偶数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.*;

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);

while (sc.hasNextInt()){
int n = sc.nextInt();
int sum = 0;
while (n > 0){
int temp = n % 10;
if (temp % 2 == 0){
sum += temp;
}
n/=10;
}
System.out.println(sum);
System.out.println();
}
}
}

打印正方形

简单的模拟题
设置result[][] 数组,检查填充,再打印

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
import java.util.*;

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
char[][] result = new char[n][n];

for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
if (i == 0 || j == 0 || i == n- 1 || j == n -1 ){
result[i][j] = '*';
} else {
result[i][j] = ' ';
}
}
}

for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
System.out.print(result[i][j]);

}
System.out.println();
}
}
}

平均绩点

String的用法
长度:.length();
比较:.equals();
切割拆分:.split();
索引:.charAt(index);
替换:.replace()
查找:.indexOf(子串);

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

import java.util.*;

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);

while (sc.hasNextLine()){
String[] n = sc.nextLine().split(" ");
int sum = 0, count = 0;

int flag = 0;
for (int i = 0; i < n.length; i++){
if (n[i].equals("A")){
sum += 4;
count++;
} else if (n[i].equals("B")){
sum += 3;
count++;
} else if (n[i].equals("C")){
sum += 2;
count++;
} else if (n[i].equals("D")){
sum += 1;
count++;
} else if (n[i].equals("F")){
sum += 0;
count++;
} else {
flag = 1;
}
}

if (flag == 1){
System.out.println("Unknown");
} else {
System.out.printf("%.2f", (double) sum / count);
System.out.println();
}


}
}
}

句子缩写

Character.toUpperCase()
Character.toLowerCase();

String.trim() delete extra space

延伸删除额外的space的写法

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

import java.util.*;

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);

while (sc.hasNextInt()){
int n = sc.nextInt();
sc.nextLine();

while (n-- > 0){
String result = removeExtraSpaces(sc.nextLine());
StringBuilder sb = new StringBuilder();
int i = 0;
sb.append(Character.toUpperCase(result.charAt(0)));
while (i < result.length()){
if (result.charAt(i) == ' '){
sb.append(Character.toUpperCase(result.charAt(i + 1)));
}

i++;

// while (i < result.length() && result.charAt(i) != ' ') i++;
// while (i < result.length() && result.charAt(i) == ' ') i++;
}

System.out.println(sb);
}
}
}

private static String removeExtraSpaces(String s){

int left = 0;
int right = s.length() - 1;

while (left < s.length() && s.charAt(left) == ' ') left++;
while (right >= 0 && s.charAt(right) == ' ') right--;
StringBuilder sb = new StringBuilder();

while (left <= right){
if(s.charAt(left) != ' ' || sb.charAt(sb.length()-1) != ' '){
sb.append(s.charAt(left));
}
left++;
}

return sb.toString();
}
}

位置互换

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
import java.util.*;

public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);

while (sc.hasNextInt()){
int n = sc.nextInt();
sc.nextLine();

while (n-- > 0){
String s = sc.nextLine();
char[] str = s.toCharArray();
int i = 0;
while (i < s.length()){
swapInArray(str, i, i+1);
i += 2;
}
System.out.println(new String(str));

}



}
}

private static void swapInArray(char[] str, int m, int n){
str[m] ^= str[n];
str[n] ^= str[m];
str[m] ^= str[n];
}
}

28 实现strStr()

https://leetcode.com/problems/find-the-index-of-the-first-occurrence-in-a-string/description/

KMP经典题目
当字符串不匹配时,可以记录一部分之前已经匹配的文本内容,避免从头匹配
主要是字符串匹配

前缀表也就是next数组
前缀表是为了回退的,他记录了模式串与主串不匹配时,模式串应该从哪里开始重新匹配
前缀表的任务是当前位置匹配失败,找到之前已经匹配上的位置,再重新匹配
记录下标i之前包括i的字符串中,有多大长度的相同前缀后缀

前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串
后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串

前缀表要求的就是相同前后缀的长度
需要做到最长相等前后缀长度

如何计算前缀表
以aabaaf举例
a 最长相同前后缀长度 0
aa 最长相同前后缀长度 1 - a
aab 最长相同前后缀长度 0
aaba 最长相同前后缀长度 1 - a
aabaa 最长相同前后缀长度 2 - aa
aabaaf 最长相同前后缀长度 0

模式串和前缀表对应位置的数字表示的就是下标i前包括i的字符串中,有多大长度的相同前缀后缀
找到不匹配的位置,找前一个字符的前缀表数值,前一个前缀表数值是2,所以把下标移动到下标2的位置继续匹配

前缀表就是next数组,实现方式有两种,一种是前缀表,另一种是前缀表统一减一(右移一位,初始位置是-1)

使用next数组进行匹配,匹配过程见gif图

具体解法:

  1. 滑动窗口,简单的双指针

一个指针指长string开头,一个指针指匹配string开头
找不到,就重新在长string中找下一个可以匹配的开头

注意在对比两个string的时候,需要判断index的大小和string的长度

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
class Solution{
public int strStr(String haystack, String needle){

// setup length variable
int m = haystack.length();
int n = needle.length();

// easy check length
if(m < n){
return -1;
}

//set two pointers
int i = 0;
int j = 0;

// set maximum i
while (i < m - n + 1){
// change haystack pointer to the first equal character of needle
while (i < m && haystack.charAt(i) != needle.charAt(j)) i++;

// if there is no needle first character return
if (i == m){
return -1;
}

// compare each character in haystack and needle
while (i < m && j < n && haystack.charAt(i) == needle.charAt(j)){
i++;
j++;
}

// if complete needle is found
if (j == n){
return i - j;
} else {
// if needle is not found, return i to the next postion of the start
i = i - j + 1;
j = 0;
}

}

return -1;

}
}

  1. KMP算法,前缀减一法实现
  2. 定义getNext函数构建next数组(计算模式串s,前缀表的过程)
    1. 初始化
      定义两个指针i和j,j指向前缀末尾位置,i指向后缀位置
      对next的初始化赋值,第一个值为-1
    2. 处理前后缀不相同的情况
      因为j初始化为-1, i从1开始,进行是s[i] 和是s[j+1]的比较
      如果遇到s[i] 与 s[j+1]不相同 向前回退
      s[i] 与 s[j+1] 不相同,就要找 j+1前一个元素在next数组里的值(就是next[j])
    3. 处理前后缀相同的情况
      s[i] 与 s[j + 1] 相同,那么就同时向后移动i 和j 说明找到了相同的前后缀,同时还要将j(前缀的长度)赋给next[i]

  1. 得到next数组之后,进行匹配
    定义两个下标j指向模式串起点, i指向文本串起始位置
    j 从 -1开始
    i 从 0开始
    s[i] 与 s[j+1] 比较
  2. 如果不相同,j从next数组中找下一个匹配的位置
  3. 相同,i和j同时向后移动
    如果j指向模式串的末尾,说明匹配到了,返回i-模式串长度 + 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
class Solution {
public int strStr(String haystack, String needle) {
int j = -1;
int[] next = getNext(needle);

for (int i = 0; i < haystack.length(); i++){
while (j >= 0 && haystack.charAt(i) != needle.charAt(j + 1)){
j = next[j];
}

if (haystack.charAt(i) == needle.charAt(j + 1)){
j++;
}

if (j == needle.length() - 1){
return i - needle.length() + 1;
}
}

return -1;
}
private int[] getNext(String s){
int[] next = new int[s.length()];
int j = -1;
next[0] = j;

for(int i = 1; i < s.length(); i++){
while (j >= 0 && s.charAt(i) != s.charAt(j + 1)){
j = next[j];
}

if (s.charAt(i) == s.charAt(j + 1)){
j++;
}

next[i] = j;
}

return next;
}
}

459 重复的子字符串

在一个串中查找是否出现过另一个串,就是kmp
再由重复子串组成的字符串中,最长相等前后缀不包含的子串,就是最小重复子串

首先找到next数组,找到最长相等前后缀不包含的子串
然后判断字符串的长度和最小重复子串之间的关系
如果字符串的长度 - 最长相等前后缀长度可以整除字符串长度,则说明可以构成

主要理解最长相等前后缀的子串,与最小重复子串之间的关系

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
class Solution{
public boolean repeatedSubstringPattern(String s){
int[] next = getNext(s);
if (next[s.length() - 1] != -1 && s.length() % (s.length() - (next[s.length() - 1] + 1)) == 0) return true;
return false;
}

private int[] getNext(String s){
int j = -1;
int[] next = new int[s.length()];

for (int i = 1; i < s.length(); i++){
while(j >= 0 && s.charAt(i) != s.charAt(j+1)){
j = next[j];
}

if (s.charAt(i) == s.charAt(j+1)){
j++;
}

next[i] = j;
}

return next;
}
}

双指针复习

27. 移除元素

讨论需不需要判断右边的元素

  1. 判断右边的元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class Solution{
    public int removeElement(int[] nums, int val){
    int left = 0;
    int right = nums.length - 1;
    while (right >= 0 && nums[right] == val) right--;
    while (left <= right){
    if (nums[left] == val) {
    nums[left] = nums[right--];
    }
    left++;
    while (right >= 0 && nums[right] == val) right--;
    }

    return left;
    }
    }

  2. 不判断右边的元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Solution{
    public int removeElement(int[] nums, int val){
    int left = 0;
    int right = nums.length - 1;

    while (left <= right){
    if (nums[left] == val){
    nums[left] = nums[right--];
    } else{
    left++;
    }
    }

    return left;
    }

    }

  3. 快慢指针

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Solution{
    public int removeElement(int[] nums, int val){
    int slowIndex = 0;
    for (int fastIndex = 0; fastIndex < nums.length; fastIndex++){
    if (nums[fastIndex] != val){
    nums[slowIndex++] = nums[fastIndex];
    }
    }
    return slowIndex;
    }
    }

344 反转字符串

头尾指针,然后同时缩进
in-place swap的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution{
public void reverseString(char[] s){
int left = 0, right = s.length - 1;
while (left < right) swapInArray(s, left++, right--);
}

private void swapInArray(char[] s, int m, int n){
s[m] ^= s[n];
s[n] ^= s[m];
s[m] ^= s[n];
}
}

替换数字

java解法不需要双指针
C++解法需要inplace扩充数组大小,在尾部需要指针,实时扩充array大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
StringBuilder sb = new StringBuilder();

String s = sc.nextLine();

for (int i = 0; i < s.length(); i++){
if (Character.isDigit(s.charAt(i))){
sb.append("number");
} else {
sb.append(s.charAt(i));
}
}

System.out.println(sb);
}
}

151 翻转字符串里的单词

先去掉多余空格
再完全反转字符串,再通过双指针,确定每个单词的位置进行翻转

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
class Solution {
public String reverseWords(String s) {
char[] str = removeExtraSpaces(s);
reverseList(str, 0, str.length - 1);

int left = 0, right = 0;
while (left < str.length){
while(right < str.length && str[right] != ' ') right++;
reverseList(str, left, right - 1);

right++;
left = right;
}

return new String(str);

}

private char[] removeExtraSpaces(String s){
char[] str = s.toCharArray();
int left = 0, right = str.length - 1;
while(left < str.length && str[left] == ' ') left++;
while(right >= 0 && str[right] == ' ') right--;
StringBuilder sb = new StringBuilder();
while (left <= right){
if (str[left] != ' ' || sb.charAt(sb.length() - 1) != ' '){
sb.append(str[left]);
}

left++;
}

return sb.toString().toCharArray();
}
private void reverseList(char[] s, int m, int n){
int left = m, right = n;
while (left < right) swapInArray(s, left++, right--);
}
private void swapInArray(char[] s, int m, int n){
s[m] ^= s[n];
s[n] ^= s[m];
s[m] ^= s[n];
}
}

206 反转链表

从前往后递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution{
public ListNode reverseList(ListNode head){
return reverse(null, head);
}

private ListNode reverse(ListNode prev, ListNode cur){
if (cur == null) return prev;

ListNode temp = cur.next;
cur.next = prev;

return reverse(cur, temp);
}
}

从后往前递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null) return null;
if (head != null && head.next == null) return head;

ListNode last = reverseList(head.next);

head.next.next = head;
head.next = null;

return last;

}
}

简单的双指针
把cur指针的node一个一个连接到prev上面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null) return head;

ListNode prev = null;

ListNode cur = head;

while (cur != null){
ListNode temp = cur.next;
cur.next = prev;
prev = cur;
cur = temp;
}

return prev;

}
}

19 删除链表的倒数第N个节点

快慢指针找倒数N个节点

快指针先走N步
慢指针再和快指针一起走到快指针到底
慢指针的node就是N个节点

删除N个节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution{
public ListNode removeNthFromEnd(ListNode head, int n){
ListNode dummy = new ListNode(0, head);

ListNode slowIndex = dummy;
ListNode fastIndex = dummy;

for (int i = 0; i <= n; i++) fastIndex = fastIndex.next;

while(fastIndex != null){
fastIndex = fastIndex.next;
slowIndex = slowIndex.next;
}

slowIndex.next = slowIndex.next.next;

return dummy.next;
}
}

160 链表相交

找到两个链表的长度,然后把长的指针移到和短的指针一样的长度,之后比较每一个node

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
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int sizeA = 0;
int sizeB = 0;
ListNode curA = headA;
ListNode curB = headB;

while (curA != null){
curA = curA.next;
sizeA++;
}

while (curB != null){
curB = curB.next;
sizeB++;
}

if (sizeA < sizeB){
ListNode temp = headA;
headA = headB;
headB = temp;

sizeA ^= sizeB;
sizeB ^= sizeA;
sizeA ^= sizeB;
}

curA = headA;
curB = headB;

for(int i = 0; i < sizeA - sizeB; i++){
curA = curA.next;
}

while (curA != null){
if (curA == curB){
return curA;
} else {
curA = curA.next;
curB = curB.next;
}
}
return null;
}
}

142 环形链表II

快慢指针找环,一个走两步一个走一步,相交即为有环
之后从头和相交点同时出发,相交点即为环入口

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 Solution {
public ListNode detectCycle(ListNode head) {
ListNode slowIndex = head;
ListNode fastIndex = head;

while (fastIndex != null && fastIndex.next != null){
fastIndex = fastIndex.next.next;
slowIndex = slowIndex.next;
if (fastIndex == slowIndex){
ListNode index1 = slowIndex;
ListNode index2 = head;

while (index1 != index2){
index1 = index1.next;
index2 = index2.next;
}

return index1;
}
}

return null;


}
}

15 三数之和

在i不变的情况下,双指针滑动找3数之和,还有需要去重

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
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> sums = new ArrayList<>();

Arrays.sort(nums);

for (int i = 0; i < nums.length; i++){
if (nums[i] > 0){
return sums;
}

if (i > 0 && nums[i] == nums[i-1]) continue;

int left = i + 1;
int right = nums.length - 1;

while (left < right){
int sum = nums[i] + nums[left] + nums[right];

if (sum > 0){
right--;
} else if (sum < 0){
left++;
} else {
sums.add(Arrays.asList(nums[i], nums[left], nums[right]));
while (right > left && nums[left] == nums[left + 1]) left++;
while (right > left && nums[right] == nums[right - 1]) right--;
right--;
left++;
}
}

}

return sums;

}
}

18 四数之和

在三数之和的基础上再嵌套一层for loop

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 Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++){
if (nums[i] > 0 && nums[i] > target){
return result;
}
if (i > 0 && nums[i] == nums[i-1]) continue;
for (int j = i+1; j < nums.length; j++){
if (j > i+1 && nums[j] == nums[j-1]) continue;

int left = j + 1;
int right = nums.length - 1;

while (right > left){
int sum = nums[left] + nums[right] + nums[i] + nums[j];
if (sum > target){
right--;
} else if (sum < target){
left++;
} else {
result.add(Arrays.asList(nums[i],nums[j], nums[left],nums[right]));
while(right > left && nums[left] == nums[left+1]) left++;
while (right > left && nums[right] == nums[right - 1]) right--;

left++;
right--;
}
}
}
}

return result;

}
}

344 反转字符串

https://leetcode.com/problems/reverse-string/description/

双指针解法 把左右交换即可

两种交换方法,一种是常见交换,一种是位运算

  1. 常见交换
    int temp = a;
    a = b;
    b = temp;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution{
public void reverseString(char[] s){
int left = 0, right = s.length - 1;
while (left < right){
swapInArray(s,left++,right--);
}
}

private void swapInArray(char[] s, int m, int n){
char temp = s[m];
s[m] = s[n];
s[n] = temp;
}
}
  1. 位运算
    a ^= b //a = a ^ b

b ^= a //b = a ^ b ^ a = a, a = a ^ b
a ^= b //a = a ^ b ^ b = b, b = a
//完成转化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution{
public void reverseString(char[] s){
int left = 0;
int right = s.length - 1;

while (left < right){
swapInArray(s, left++, right--);
}
}

private void swapInArray(char[] s, int m, int n){
s[m] ^= s[n];
s[n] ^= s[m];
s[m] ^= s[n];
}
}

541 反转字符串II

https://leetcode.com/problems/reverse-string-ii/description/

使用指针做法,查找k的位置,reverse方法记住,在外面套一层逻辑来判断什么需要反转什么不需要反转,要背下来,在array中区间反转逻辑

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
class Solution{
public String reverseStr(String s, int k) {
char[] str = s.toCharArray();

int left = 0;
while (left < str.length){
int right = left + k - 1;
if (right > str.length - 1){
reverseList(str, left, str.length - 1);
} else {
reverseList(str, left, right);
}

left += 2 * k;
}

return new String(str);

}

private void reverseList(char[] str, int m, int n){
int left = m, right = n;
while (left < right) swapInArray(str, left++, right--);
}

private void swapInArray(char[] str, int m, int n){
str[m] ^= str[n];
str[n] ^= str[m];
str[m] ^= str[n];
}

}

替换数字

https://kamacoder.com/problempage.php?pid=1064
由于c++可以扩充数组,则可使用双指针记录读到数字的位置,然后把在数组最后的指针向后移出number的位置,但是java更简单,可以直接用Stringbuilder做
注意代码用法:

  1. Character.isDigit
  2. Scanner 用法
  3. StringBuilder 用法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import java.util.*;
    public class Main{
    public static void main(String[] args){
    Scanner sc = new Scanner(System.in);
    String s = sc.nextLine();

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < s.length(); i++){
    if (Character.isDigit(s.charAt(i))){
    sb.append("number");
    } else {
    sb.append(s.charAt(i));
    }
    }

    System.out.println(sb);

    sc.close();
    }
    }

151 翻转字符串里的单词

https://leetcode.com/problems/reverse-words-in-a-string/
要求不使用辅助空间,空间复杂度为O(1)
具体实现:
先移除多余的空格,后反转整个字符串,之后在单独反转单词部分

  1. 移除多余空格,使用StringBuilder 重构string
    先把开头和结尾的空格去掉
    在append不是空格的部分,遇到空格是检查StringBuilder上一个元素是否为空格,不是就添加,是就跳过
    返回StringBuilder
  2. 整个数组反转
    in-place: 双指针互换左右字符
    StringBuilder: 重建StringBuilder,设置左右数
  3. 反转各个单词
    利用快慢指针,找到每个单词的起始和终止位置,然后in-place翻转
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 String reverseWords(String s) {
char[] str = removeExtraSpaces(s);
reverseList(str, 0, str.length - 1);
System.out.println(str.length);
int left = 0;
int right = 1;
while (left < str.length){
while(right < str.length && str[right] != ' '){
right++;
}
reverseList(str, left, right - 1);
left = right + 1;
right++;
}
return new String(str);
}

private char[] removeExtraSpaces(String s){
StringBuilder sb = new StringBuilder();
int left = 0, right = s.length() - 1;
while (left < s.length() && s.charAt(left) == ' ') left++;
while (right > 0 && s.charAt(right) == ' ') right--;

while (left <= right){
if (s.charAt(left) != ' ' || sb.charAt(sb.length() - 1) != ' '){
sb.append(s.charAt(left));
}

left++;
}
return sb.toString().toCharArray();
}

private void reverseList(char[] str, int m, int n){
int left = m, right = n;

while (left < right){
swapInArray(str, left++, right--);
}
}

private void swapInArray(char[] str, int m, int n){
str[m] ^= str[n];
str[n] ^= str[m];
str[m] ^= str[n];
}
}


右旋字符串

https://kamacoder.com/problempage.php?pid=1065

解法与翻转字符串里的单词差不多
把整体反转之后,再把局部的长度反转,得到结果

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
class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
sc.nextLine();
String s = sc.nextLine();
char[] str = s.toCharArray();

reverseList(str, 0, s.length()-1);
reverseList(str, 0, n-1);
reverseList(str, n, s.length()-1);
System.out.println(str);


sc.close();
}

private static void reverseList(char[] str, int m, int n){
int left = m, right = n;
while(left < right){
swapInArray(str, left++, right--);
}
}

private static void swapInArray(char[] str, int m, int n){
str[m] ^= str[n];
str[n] ^= str[m];
str[m] ^= str[n];
}
}

0%