LeetCode

第一天 | 力扣 704 & 27

数组

数组是存放在连续内存空间上的相同类型数据的集合

需要注意:

  • 数组下标都是从 0 开始的
  • 数组内存空间的地址是连续的
  • 数组的元素是不能删的,只能覆盖

因为数组在内存空间的地址是连续的,所以在删除或者增添元素的时候。难免需要移动其它元素的地址

704、二分查找

题目

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1

链接:https://leetcode.cn/problems/binary-search/

示例

示例 1:

1
2
3
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:

1
2
3
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

提示:

  1. 你可以假设 nums 中的所有元素是不重复的。
  2. n 将在 [1, 10000]之间。
  3. nums 的每个元素都将在 [-9999, 9999]之间。

解题

左闭右闭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right){
int mid = (left + right) / 2;
int num = nums[mid];
if(num > target){
right = mid - 1;
}else if(num < target){
left = mid + 1;
}else{
return mid;
}
}
return -1;
}
}

左闭右开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left < right){
int mid = (left + right) / 2;
int num = nums[mid];
if(num > target){
right = mid;
}else if(num < target){
left = mid + 1;
}else{
return mid;
}
}
return -1;
}
}

27、移除元素

题目

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

链接https://leetcode.cn/problems/remove-element/

示例

示例 1:

1
2
3
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

示例 2:

1
2
3
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,3,0,4]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

解题

双指针

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

暴力解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public int removeElement(int[] nums, int val) {
int len = nums.length;
for(int i = 0; i < len; i++){
if(nums[i] == val){
for(int j = i + 1; j < len; j++){
nums[j - 1] = nums[j];
}
i--;
len--;
}
}
return len;
}
}

第二天 | 力扣 977 & 209 & 59

977、有序数组的平方

题目

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

链接:https://leetcode.cn/problems/squares-of-a-sorted-array/

示例

示例 1:

1
2
3
4
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

示例 2:

1
2
输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]

解题

双指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int[] sortedSquares(int[] nums) {
int left = 0;
int right = nums.length - 1;
int[] result = new int[nums.length];
int index = nums.length - 1;
while(left <= right){
if(nums[left] * nums[left] > nums[right] * nums[right]){
result[index] = nums[left] * nums[left];
index --;
left ++;
}else{
result[index] = nums[right] * nums[right];
index --;
right --;
}
}
return result;
}
}

暴力解法

1
2
3
4
5
6
7
8
9
10
class Solution {
public int[] sortedSquares(int[] nums) {
int[] result = new int[nums.length];
for(int i = 0; i < nums.length; i++){
result[i] = nums[i] * nums[i];
}
Arrays.sort(result);
return result;
}
}

209、长度最小的子数组

题目

给定一个含有 n 个正整数的数组和一个正整数 target

找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度**。**如果不存在符合条件的子数组,返回 0

链接:https://leetcode.cn/problems/minimum-size-subarray-sum/

示例

示例 1:

1
2
3
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:

1
2
输入:target = 4, nums = [1,4,4]
输出:1

示例 3:

1
2
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

解题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left = 0;
int sum = 0;
int result = Integer.MAX_VALUE;
// 放大窗口
for(int right = 0; right < nums.length; right++){
sum += nums[right];
// 缩小窗口
while(sum >= target){
result = Math.min(result, right - left + 1);
sum -= nums[left];
left++;
}
}
return result == Integer.MAX_VALUE ? 0 : result;
}
}

59、螺旋矩阵Ⅱ

题目

给你一个正整数 n ,生成一个包含 1n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix

链接:https://leetcode.cn/problems/spiral-matrix-ii/

示例

示例 1:

img

1
2
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]

示例 2:

1
2
输入:n = 1
输出:[[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
class Solution {
public int[][] generateMatrix(int n) {
int start = 0;
int count = 1;
int loop = 0;
int i, j;
int[][] res = new int[n][n];
while(loop++ < n / 2){
//上侧从左到右
for(j = start; j < n - loop; j++){
res[start][j] = count++;
}
//右侧从上到下
for(i = start; i < n - loop; i++){
res[i][j] = count++;
}
//下侧从右往左
for(;j >= loop; j--){
res[i][j] = count++;
}
//右侧从下往上
for(;i >= loop; i--){
res[i][j] = count++;
}
start++;
}
if(n % 2 == 1){
res[start][start] = count;
}
return res;
}
}

第三天 | 力扣 203 & 707 & 206

链表

链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向 null(空指针的意思)

链表的入口节点称为链表的头节点也就是 head

链表的类型

单链表

  • 刚刚说的就是单链表

双链表

  • 单链表中的指针域只能指向节点的下一个节点
  • 双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点
  • 双链表既可以向前查询,也可以向后查询

循环链表

  • 循环链表,就是链表首尾相连
  • 循环链表可以用来解决约瑟夫环问题

链表的存储方式

  • 数组是在内存中连续分布的,但是链表在内存中可不是连续分布的

  • 链表是通过指针域的指针链接在内存中各个节点

所以,链表中的节点在内存中不是连续分布的,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理

性能分析

把链表的特性和数组的特性进行一个对比

插入/删除(时间复杂度) 查询(时间复杂度) 适用场景
数组 O(n) O(1) 数据量固定,频繁查询,较少增删
链表 O(1) O(n) 数据量不固定,频繁增删,较少查询

数组在定义的时候,长度就是固定的,如果想改动数组的长度,就需要重新定义一个新的数组

链表的长度可以是不固定的,并且可以动态增删,适合数据量不固定,频繁增删,较少查询的场景

203、移除链表元素

题目

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

链接:https://leetcode.cn/problems/remove-linked-list-elements/

示例

示例 1:

img

1
2
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:

1
2
输入:head = [], val = 1
输出:[]

示例 3:

1
2
输入:head = [7,7,7,7], val = 7
输出:[]

题解

直接使用原链表

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
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
// 头结点不为空
while(head != null && head.val == val){
// 删除元素
head = head.next;
}
// 定义临时指针
ListNode cur = head;
while(cur != null && cur.next != null){
if(cur.next.val == val){
cur.next = cur.next.next;
}else{
cur = cur.next;
}
}
return head;
}
}

设置一个虚拟头结点

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
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
// 定义虚拟头结点
ListNode dummyHead = new ListNode(-1, head);
dummyHead.next = head;
ListNode cur = dummyHead;
while(cur.next != null){
if(cur.next.val == val){
cur.next = cur.next.next;
}else{
cur = cur.next;
}
}
return dummyHead.next;
}
}

707、设计链表

题目

你可以选择使用单链表或者双链表,设计并实现自己的链表。

单链表中的节点应该具备两个属性:valnextval 是当前节点的值,next 是指向下一个节点的指针/引用。

如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。

实现 MyLinkedList 类:

  • MyLinkedList() 初始化 MyLinkedList 对象。
  • int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1
  • void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
  • void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
  • void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
  • void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。

链接:https://leetcode.cn/problems/design-linked-list/

示例

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
输入
["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]

解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2); // 链表变为 1->2->3
myLinkedList.get(1); // 返回 2
myLinkedList.deleteAtIndex(1); // 现在,链表变为 1->3
myLinkedList.get(1); // 返回 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
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
class ListNode {
int val;
ListNode next;
ListNode(){}
ListNode(int val) {
this.val=val;
}
}

class MyLinkedList {
// size存储链表元素的个数
int size;
// 虚拟头结点
ListNode head;

public MyLinkedList() {
size = 0;
head = new ListNode(0);
}

public int get(int index) {
if(index < 0 || index >= size){
return -1;
}
ListNode cur = head;
for(int i = 0; i <= index; i++){
cur = cur.next;
}
return cur.val;
}

public void addAtHead(int val) {
addAtIndex(0, val);
}

public void addAtTail(int val) {
addAtIndex(size, val);
}

public void addAtIndex(int index, int val) {
if(index > size){
return;
}
if(index < 0){
index = 0;
}
size++;
ListNode pre = head;
for(int i = 0; i < index; i++){
pre = pre.next;
}
ListNode add = new ListNode(val);
add.next = pre.next;
pre.next = add;
}

public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
size--;
if (index == 0) {
head = head.next;
return;
}
ListNode pred = head;
for (int i = 0; i < index ; i++) {
pred = pred.next;
}
pred.next = pred.next.next;
}
}

/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList obj = new MyLinkedList();
* int param_1 = obj.get(index);
* obj.addAtHead(val);
* obj.addAtTail(val);
* obj.addAtIndex(index,val);
* obj.deleteAtIndex(index);
*/

206、反转链表

题目

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

链接:https://leetcode.cn/problems/reverse-linked-list/

示例

示例 1:

img

1
2
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

img

1
2
输入:head = [1,2]
输出:[2,1]

示例 3:

1
2
输入:head = []
输出:[]

题解

双指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode cur = head;
ListNode pre = null;
ListNode temp;
while(cur != null){
temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}

递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(null, head);
}
private ListNode reverse(ListNode pre, ListNode cur){
ListNode temp;
if(cur == null){
return pre;
}
temp = cur.next;
cur.next = pre;
return reverse(cur, temp);
}
}

第四天 | 力扣 24 & 19 & 160 & 142

24、两两交换链表中的节点

题目

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

链接:https://leetcode.cn/problems/swap-nodes-in-pairs/

示例

示例 1:

img

1
2
输入:head = [1,2,3,4]
输出:[2,1,4,3]

示例 2:

1
2
输入:head = []
输出:[]

示例 3:

1
2
输入:head = [1]
输出:[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
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummyhead = new ListNode(-1); // 设置虚拟头结点
dummyhead.next = head; // 将虚拟头结点指向head
ListNode cur = dummyhead;
while(cur.next != null && cur.next.next != null){
ListNode temp = cur.next;
ListNode temp1 = cur.next.next.next;
cur.next = cur.next.next;
cur.next.next = temp;
temp.next = temp1;
cur = cur.next.next;
}
return dummyhead.next;

}
}

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

题目

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/

示例

示例 1:

img

1
2
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

1
2
输入:head = [1], n = 1
输出:[]

示例 3:

1
2
输入:head = [1,2], n = 1
输出:[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
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyhead = new ListNode(-1);
dummyhead.next = head; // 将虚拟头结点指向head
ListNode fast = dummyhead;
ListNode slow = dummyhead;
for(int i = 0; i <= n; i++){
fast = fast.next;
}
while(fast != null){
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummyhead.next;

}
}

160、链表相交

题目

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null

图示两个链表在节点 c1 开始相交**:**

img

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/

示例

示例 1:

img

1
2
3
4
5
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

img

1
2
3
4
5
输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

img

1
2
3
4
5
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB;
int lenA = 0, lenB = 0;
while(curA != null){
lenA++;
curA = curA.next;
}
while(curB != null){
lenB++;
curB = curB.next;
}
curA = headA;
curB = headB;
if(lenB > lenA){
int tempLen = lenA;
lenA = lenB;
lenB = tempLen;

ListNode tempNode = curA;
curA = curB;
curB = tempNode;
}
int gap = lenA - lenB;
while(gap-- > 0){
curA = curA.next;
}
while(curA != null){
if(curA == curB){
return curA;
}
curA = curA.next;
curB = curB.next;
}
return null;
}
}

142、环形链表Ⅱ

题目

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

链接:https://leetcode.cn/problems/linked-list-cycle-ii/

示例

示例 1:

img

1
2
3
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

img

1
2
3
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

img

1
2
3
输入:head = [1], pos = -1
输出:返回 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
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
ListNode pos1 = fast;
ListNode pos2 = head;
while(pos1 != pos2){
pos1 = pos1.next;
pos2 = pos2.next;
}
return pos1;
}
}
return null;
}
}

第五天 | 休息

第六天 | 力扣 242 & 349 & 202 & 1

哈希表

哈希表是根据关键码的值而直接进行访问的数据结构

哈希表能解决什么问题呢,一般哈希表都是用来快速判断一个元素是否出现在集合里

哈希碰撞

  • 拉链法:拉链法就是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间
  • 线性探测法:一定要保证 tableSize > dataSize。需要依靠哈希表中的空位来解决碰撞问题

常见的三种哈希结构

  • 数组
  • set (集合)
  • map (映射)

当遇到了一个要快速判断一个元素是否出现集合里的时候,就要考虑哈希法

但是哈希法也是牺牲了空间换取了时间,因为要使用额外的数组,set 或者是 map 来存放数据,才能实现快速的查找

242、有效的字母异位词

题目

给定两个字符串 *s**t* ,编写一个函数来判断 *t* 是否是 *s* 的字母异位词。

**注意:**若 *s**t* 中每个字符出现的次数都相同,则称 *s**t* 互为字母异位词。

链接:https://leetcode.cn/problems/valid-anagram/

示例

示例 1:

1
2
输入: s = "anagram", t = "nagaram"
输出: true

示例 2:

1
2
输入: s = "rat", t = "car"
输出: false

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public boolean isAnagram(String s, String t) {
int[] hash = new int[26];
for(int i = 0; i < s.length(); i++){
hash[s.charAt(i) - 'a']++;
}
for(int i = 0; i < t.length(); i++){
hash[t.charAt(i) - 'a']--;
}
for (int i = 0; i < 26; i++) {
if (hash[i] != 0) {
return false;
}
}
return true;
}
}

349、两个数组的交集

题目

给定两个数组 nums1nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序

链接:https://leetcode.cn/problems/intersection-of-two-arrays/

示例

示例 1:

1
2
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

示例 2:

1
2
3
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
HashSet<Integer> resultSet = new HashSet<>();
int[] hash = new int[1000];

for(int i = 0; i < nums1.length; i++){
hash[nums1[i]] = 1;
}
for(int i = 0; i < nums2.length; i++){
if(hash[nums2[i]] == 1){
resultSet.add(nums2[i]);
}
}
int[] res = new int[resultSet.size()];
int index = 0;
for(int num : resultSet){
res[index++] = num;
}
return res;
}
}

202、快乐数

题目

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n快乐数 就返回 true ;不是,则返回 false

链接:https://leetcode.cn/problems/happy-number/

示例

示例 1:

1
2
3
4
5
6
7
输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

示例 2:

1
2
输入:n = 2
输出:false

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public boolean isHappy(int n) {
Set<Integer> hash = new HashSet<>();
while(n != 1 && !hash.contains(n)){
hash.add(n);
n = getNextNumber(n);
}
return n == 1;
}

private int getNextNumber(int n){
int res = 0;
while(n > 0){
int temp = n % 10;
res += temp * temp;
n = n / 10;
}
return res;
}
}

1、两数之和

题目

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

链接:https://leetcode.cn/problems/two-sum/

示例

示例 1:

1
2
3
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

1
2
输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

1
2
输入:nums = [3,3], target = 6
输出:[0,1]

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int[] twoSum(int[] nums, int target) {
int res[] = new int[2];
if(nums == null || nums.length == 0){
return res;
}
Map<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < nums.length; i++){
int temp = target - nums[i];
if(map.containsKey(temp)){
res[1] = i;
res[0] = map.get(temp);
break;
}
map.put(nums[i], i);
}
return res;
}
}

第七天 | 力扣 454 & 383 & 15 & 18

454、四数相加

题目

给你四个整数数组 nums1nums2nums3nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

  • 0 <= i, j, k, l < n
  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

链接:https://leetcode.cn/problems/4sum-ii/

示例

示例 1:

1
2
3
4
5
6
输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释:
两个元组如下:java
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0

示例 2:

1
2
输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
输出:1

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int res = 0;
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for(int i : nums1){
for(int j : nums2){
int sum = i + j;
map.put(sum, map.getOrDefault(sum, 0) + 1);
}
}
for(int i : nums3){
for(int j : nums4){
res += map.getOrDefault(0 - i - j, 0);
}
}
return res;
}
}

383、赎金信

题目

给你两个字符串:ransomNotemagazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false

magazine 中的每个字符只能在 ransomNote 中使用一次。

链接:https://leetcode.cn/problems/ransom-note/

示例

示例 1:

1
2
输入:ransomNote = "a", magazine = "b"
输出:false

示例 2:

1
2
输入:ransomNote = "aa", magazine = "ab"
输出:false

示例 3:

1
2
输入:ransomNote = "aa", magazine = "aab"
输出:true

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int[] hash = new int[26];
for(int i = 0; i < ransomNote.length(); i++){
hash[ransomNote.charAt(i) - 'a']++;
}
for(int i = 0; i < magazine.length(); i++){
hash[magazine.charAt(i) - 'a']--;
}
for (int i = 0; i < 26; i++) {
if (hash[i] > 0) {
return false;
}
}
return true;
}
}

15、三数之和

题目

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

**注意:**答案中不可以包含重复的三元组。

链接:https://leetcode.cn/problems/3sum/

示例

示例 1:

1
2
3
4
5
6
7
8
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

1
2
3
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0

示例 3:

1
2
3
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0

题解

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<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);

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

if (i > 0 && nums[i] == nums[i - 1]) { // 去重a
continue;
}

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

right--;
left++;
}
}
}
return result;
}
}

18、四数之和

题目

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abcd 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

链接:https://leetcode.cn/problems/4sum/

示例

示例 1:

1
2
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例 2:

1
2
输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,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
36
37
38
39
40
41
42
43
44
45
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++) {

// nums[i] > target 直接返回, 剪枝操作
if (nums[i] > 0 && nums[i] > target) {
return result;
}

if (i > 0 && nums[i - 1] == nums[i]) { // 对nums[i]去重
continue;
}

for (int j = i + 1; j < nums.length; j++) {

if (j > i + 1 && nums[j - 1] == nums[j]) { // 对nums[j]去重
continue;
}

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

left++;
right--;
}
}
}
}
return result;
}
}

第八天 | 力扣 344 & 541 & 54 & 151 & 55

344、反转字符串

题目

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须**原地修改输入数组**、使用 O(1) 的额外空间解决这一问题。

链接:https://leetcode.cn/problems/reverse-string/

示例

示例 1:

1
2
输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]

示例 2:

1
2
输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]

题解

1
2
3
4
5
6
7
8
9
10
class Solution {
public void reverseString(char[] s) {
int len = s.length;
for(int i = 0, j = len - 1; i < len / 2; i++, j--){
char temp = s[i];
s[i] = s[j];
s[j] = temp;
}
}
}

541、反转字符串Ⅱ

题目

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

  • 如果剩余字符少于 k 个,则将剩余字符全部反转。
  • 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

链接:https://leetcode.cn/problems/reverse-string-ii/

示例

示例 1:

1
2
输入:s = "abcdefg", k = 2
输出:"bacdfeg"

示例 2:

1
2
输入:s = "abcd", k = 2
输出:"bacd"

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public String reverseStr(String s, int k) {
char[] ch = s.toCharArray();
for(int i = 0; i < ch.length; i+= 2 * k){
if(i + k <= ch.length){
reverse(ch, i, i + k - 1);
continue;
}
reverse(ch, i, ch.length - 1);
}
return new String(ch);
}

public void reverse(char[] ch, int i, int j) {
for(; i < j; i++, j--){
char temp = ch[i];
ch[i] = ch[j];
ch[j] = temp;
}
}
}

55、替换数字

题目

给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number。 例如,对于输入字符串 “a1b2c3”,函数应该将其转换为 “anumberbnumbercnumber”。

链接:https://kamacoder.com/problempage.php?pid=1064

示例

1
2
输入:s = "a1b2c3"
输出:"anumberbnumbercnumber"

题解

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

class Main{
public static void main(String[] args){
Scanner in = new Scanner(System.in);
String s = in.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);
}
}

151、翻转字符串里的单词

题目

给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

**注意:**输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

链接:https://leetcode.cn/problems/reverse-words-in-a-string/

示例

示例 1:

1
2
输入:s = "the sky is blue"
输出:"blue is sky the"

示例 2:

1
2
3
输入:s = "  hello world  "
输出:"world hello"
解释:反转后的字符串中不能存在前导空格和尾随空格。

示例 3:

1
2
3
输入:s = "a good   example"
输出:"example good 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
28
29
30
class Solution {
public String reverseWords(String s) {
char[] ch = s.toCharArray();
int slow = 0;
for(int fast = 0; fast < ch.length; fast++){
if(ch[fast] != ' '){
if(slow != 0){
ch[slow++] = ' ';
}
int start = slow;
while (fast < ch.length && ch[fast] != ' ') {
ch[slow++] = ch[fast++];
}
reverse(ch, start, slow - 1);
}
}
reverse(ch, 0, slow - 1);
return new String(ch, 0, slow);
}

private void reverse(char[] ch, int start, int end) {
while (start < end) {
char temp = ch[start];
ch[start] = ch[end];
ch[end] = temp;
start++;
end--;
}
}
}

55、右旋转字符串

题目

字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k,请编写一个函数,将字符串中的后面 k 个字符移到字符串的前面,实现字符串的右旋转操作。

例如,对于输入字符串 “abcdefg” 和整数 2,函数应该将其转换为 “fgabcde”。

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

示例

1
2
输入:s = "abcdefg", k = 2
输出:s = "fgabcde"

题解

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.Scanner;

public class Main {

public static String rightRotateString(int k, String s) {
// 确保 k 在有效范围内
k = k % s.length();

// 将字符串分为两部分进行旋转
String rotatedStr = s.substring(s.length() - k) + s.substring(0, s.length() - k);

return rotatedStr;
}

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

// 读取输入
int k = scanner.nextInt();
scanner.nextLine(); // 消耗掉换行符
String s = scanner.nextLine().trim();

// 输出结果
String result = rightRotateString(k, s);
System.out.println(result);

scanner.close();
}
}

第九天 | 力扣 28 & 459

28、实现 strStr()

题目

给你两个字符串 haystackneedle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1

链接:https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/

示例

示例 1:

1
2
3
4
输入:haystack = "sadbutsad", needle = "sad"
输出:0
解释:"sad" 在下标 06 处匹配。
第一个匹配项的下标是 0 ,所以返回 0

示例 2:

1
2
3
输入:haystack = "leetcode", needle = "leeto"
输出:-1
解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -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
class Solution {
// 前缀表(不减一)
public int strStr(String haystack, String needle) {
if (needle.length() == 0) return 0;
int[] next = new int[needle.length()];
getNext(next, needle);

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

}

private void getNext(int[] next, String s) {
int j = 0;
next[0] = 0;
for (int i = 1; i < s.length(); i++) {
while (j > 0 && s.charAt(j) != s.charAt(i))
j = next[j - 1];
if (s.charAt(j) == s.charAt(i))
j++;
next[i] = j;
}
}
}

459、重复的子字符串

题目

给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

链接:https://leetcode.cn/problems/repeated-substring-pattern/

示例

示例 1:

1
2
3
输入: s = "abab"
输出: true
解释: 可由子串 "ab" 重复两次构成。

示例 2:

1
2
输入: s = "aba"
输出: false

示例 3:

1
2
3
输入: s = "abcabcabcabc"
输出: true
解释: 可由子串 "abc" 重复四次构成。 (或子串 "abcabc" 重复两次构成。)

题解

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 boolean repeatedSubstringPattern(String s) {
if (s.equals("")) return false;

int len = s.length();
// 原串加个空格(哨兵),使下标从1开始,这样j从0开始,也不用初始化了
s = " " + s;
char[] chars = s.toCharArray();
int[] next = new int[len + 1];

// 构造 next 数组过程,j从0开始(空格),i从2开始
for (int i = 2, j = 0; i <= len; i++) {
// 匹配不成功,j回到前一位置 next 数组所对应的值
while (j > 0 && chars[i] != chars[j + 1]) j = next[j];
// 匹配成功,j往后移
if (chars[i] == chars[j + 1]) j++;
// 更新 next 数组的值
next[i] = j;
}

// 最后判断是否是重复的子字符串,这里 next[len] 即代表next数组末尾的值
if (next[len] > 0 && len % (len - next[len]) == 0) {
return true;
}
return false;
}
}

字符串

字符串是若干字符组成的有限序列,也可以理解为是一个字符数组

  • 双指针是字符串处理的常客
  • KMP 算法是字符串查找最重要的算法

第十天 | 力扣 232 & 225

232、用栈实现队列

原理

push(x): 将一个元素放入队列的尾部

pop():从队列首部移除元素

peek():返回队列首部的元素

empty():返回队列是否为空

题目

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

  • 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

链接:https://leetcode.cn/problems/implement-queue-using-stacks/

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]

解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return 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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class MyQueue {
Stack<Integer> stackIn;
Stack<Integer> stackOut;

public MyQueue() {
stackIn = new Stack<>(); // 负责进栈
stackOut = new Stack<>(); // 负责出栈
}

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

public int pop() {
dumpstackIn();
return stackOut.pop();
}

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

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

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

/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/

225、用队列实现栈

原理

push(x):元素 x 入栈

pop():移除栈顶元素

top():获取栈顶元素

empty():返回栈是否为空

题目

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppopempty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false

注意:

  • 你只能使用队列的基本操作 —— 也就是 push to backpeek/pop from frontsizeis empty 这些操作。
  • 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

链接:https://leetcode.cn/problems/implement-stack-using-queues/

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]

解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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;
queueTemp = queue1;
queue1 = queue2;
queue2 = queueTemp;
}

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

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

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();
*/

第十一天 | 力扣 20 & 1047 & 150

20、有效的括号

题目

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

链接:https://leetcode.cn/problems/valid-parentheses/

示例

示例 1:

1
2
输入:s = "()"
输出:true

示例 2:

1
2
输入:s = "()[]{}"
输出:true

示例 3:

1
2
输入:s = "(]"
输出: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> deque = new LinkedList<>();
char ch;
for (int i = 0; i < s.length(); i++) {
ch = s.charAt(i);
if (ch == '(') {
deque.push(')');
} else if (ch == '{') {
deque.push('}');
} else if (ch == '[') {
deque.push(']');
} else if (deque.isEmpty() || deque.peek() != ch) {
return false;
} else {
deque.pop();
}
}
return deque.isEmpty();
}
}

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

题目

给出由小写字母组成的字符串 S重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

链接:https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string/

示例

1
2
3
4
输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public String removeDuplicates(String s) {
Deque<Character> deque = new LinkedList<>();
char ch;
for (int i = 0; i < s.length(); i++) {
ch = s.charAt(i);
if (deque.isEmpty() || deque.peek() != ch) {
deque.push(ch);
} else {
deque.pop();
}
}
String str = "";
while (!deque.isEmpty()) {
str = deque.pop() + str;
}
return str;
}
}

150、逆波兰表达式求值

题目

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:

  • 有效的算符为 '+''-''*''/'
  • 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
  • 两个整数之间的除法总是 向零截断
  • 表达式中不含除零运算。
  • 输入是一个根据逆波兰表示法表示的算术表达式。
  • 答案及所有中间计算结果可以用 32 位 整数表示。

链接:https://leetcode.cn/problems/evaluate-reverse-polish-notation/

示例

示例 1:

1
2
3
输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

示例 2:

1
2
3
输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6

示例 3:

1
2
3
4
5
6
7
8
9
10
输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new LinkedList();
for (String s : tokens) {
if ("+".equals(s)) {
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)) {
int temp1 = stack.pop();
int temp2 = stack.pop();
stack.push(temp2 / temp1);
} else {
stack.push(Integer.valueOf(s));
}
}
return stack.pop();
}
}

第十二天 | 休息

第十三天 | 力扣 239 & 347

239、滑动窗口最大值

题目

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值

链接:https://leetcode.cn/problems/sliding-window-maximum/

示例

示例 1:

1
2
3
4
5
6
7
8
9
10
11
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

示例 2:

1
2
输入:nums = [1], k = 1
输出:[1]

题解

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 int[] maxSlidingWindow(int[] nums, int k) {
ArrayDeque<Integer> deque = new ArrayDeque<>();
int n = nums.length;
int[] res = new int[n - k + 1];
int idex = 0;
for (int i = 0; i < n; i++) {
while (!deque.isEmpty() && deque.peek() < i - k + 1) {
deque.poll();
}
while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
deque.pollLast();
}

deque.offer(i);

if (i >= k - 1) {
res[idex++] = nums[deque.peek()];
}
}
return res;
}
}

347、前K个高频元素

题目

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

链接:https://leetcode.cn/problems/top-k-frequent-elements/

示例

示例 1:

1
2
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

1
2
输入: nums = [1], k = 1
输出: [1]

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int[] topKFrequent(int[] nums, int k) {
PriorityQueue<int[]> pq = new PriorityQueue<>((o1, o2) -> o1[1] - o2[1]);
int[] res = new int[k];
Map<Integer, Integer> map = new HashMap<>();
for(int num : nums) map.put(num, map.getOrDefault(num, 0) + 1);
for(var x : map.entrySet()){
int[] tmp = new int[2];
tmp[0] = x.getKey();
tmp[1] = x.getValue();
pq.offer(tmp);
if(pq.size() > k){
pq.poll();
}
}
for(int i = 0; i < k; i++){
res[i] = pq.poll()[0];
}
return res;
}
}

第十四天 | 力扣 144 & 145 & 94

二叉树

种类

  • 满二叉树
  • 完全二叉树
  • 二叉搜索树
  • 平衡二叉搜索树

满二叉树

如果一棵二叉树只有度为 0 的结点和度为 2 的结点,并且度为 0 的结点在同一层上,则这棵二叉树为满二叉树

也可以说深度为 k,有 2^k-1 个节点的二叉树

完全二叉树

在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h 从1开始),则该层包含 1~ 2^(h-1) 个节点

二叉搜索树

二叉搜索树是有数值的,二叉搜索树是一个有序树

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值
  • 它的左、右子树也分别为二叉排序树

平衡二叉搜索树

又被称为 AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过 1,并且左右两个子树都是一棵平衡二叉树

存储方式

  • 链式存储
  • 顺序存储

链式存储方式就用指针,顺序存储方式就是用数组

顺序存储的元素在内存是连续分布的,而链式存储则是通过指针把分布在各个地址的节点串联在一起

遍历方式

  • 深度优先遍历:先往深走,遇到叶子节点再往回走
  • 广度优先遍历:一层一层的去遍历

深度优先遍历

前序遍历(递归法,迭代法):中左右

中序遍历(递归法,迭代法):左中右

后续遍历(递归法,迭代法):左右中

广度优先遍历

层次遍历(迭代法)

定义

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

144、二叉树的前序遍历

题目

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

链接:https://leetcode.cn/problems/binary-tree-preorder-traversal/

示例

示例 1:

img

1
2
输入:root = [1,null,2,3]
输出:[1,2,3]

示例 2:

1
2
输入:root = []
输出:[]

示例 3:

1
2
输入:root = [1]
输出:[1]

示例 4:

img

1
2
输入:root = [1,2]
输出:[1,2]

示例 5:

img

1
2
输入:root = [1,null,2]
输出:[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
30
31
/**
* 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> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
preorder(root, res);
return res;
}

public void preorder(TreeNode root, List<Integer> res) {
if (root == null) {
return;
}
res.add(root.val);
preorder(root.left, res);
preorder(root.right, res);
}
}

145、二叉树的后续遍历

题目

给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历

链接:https://leetcode.cn/problems/binary-tree-postorder-traversal/

示例

示例 1:

img

1
2
输入:root = [1,null,2,3]
输出:[3,2,1]

示例 2:

1
2
输入:root = []
输出:[]

示例 3:

1
2
输入:root = [1]
输出:[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
/**
* 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> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
postorder(root, res);
return res;
}

public void postorder(TreeNode root, List<Integer> res) {
if (root == null) {
return;
}
postorder(root.left, res);
postorder(root.right, res);
res.add(root.val);
}
}

94、二叉树的中序遍历

题目

给定一个二叉树的根节点 root ,返回 它的 中序 遍历

链接:https://leetcode.cn/problems/binary-tree-inorder-traversal/

示例

示例 1:

img

1
2
输入:root = [1,null,2,3]
输出:[1,3,2]

示例 2:

1
2
输入:root = []
输出:[]

示例 3:

1
2
输入:root = [1]
输出:[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
/**
* 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> res = new ArrayList<Integer>();
inorder(root, res);
return res;
}

public void inorder(TreeNode root, List<Integer> res) {
if (root == null) {
return;
}
inorder(root.left, res);
res.add(root.val);
inorder(root.right, res);
}
}

第十五天 | 力扣 10 & 226 & 101

10、二叉树的层序遍历

题目

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

链接:https://leetcode.cn/problems/binary-tree-level-order-traversal/

示例

示例 1:

img

1
2
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

示例 2:

1
2
输入:root = [1]
输出:[[1]]

示例 3:

1
2
输入: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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
* 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<List<Integer>> resList = new ArrayList<List<Integer>>();

public List<List<Integer>> levelOrder(TreeNode root) {

Fun01(root);

return resList;
}

public void Fun01(TreeNode node) {
if (node == null) {
return;
}
Queue<TreeNode> que = new LinkedList<TreeNode>();
que.offer(node);

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

while (len > 0) {
TreeNode tmpNode = que.poll();
itemList.add(tmpNode.val);

if (tmpNode.left != null) {
que.offer(tmpNode.left);
}
if (tmpNode.right != null) {
que.offer(tmpNode.right);
}
len--;
}
resList.add(itemList);
}
}
}

226、翻转二叉树

题目

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

链接:https://leetcode.cn/problems/invert-binary-tree/

示例

示例 1:

img

1
2
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

示例 2:

img

1
2
输入:root = [2,1,3]
输出:[2,3,1]

示例 3:

1
2
输入: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
30
31
32
/**
* 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 TreeNode invertTree(TreeNode root) {
if(root == null){
return null;
}
invertTree(root.left);
invertTree(root.right);
swapChildren(root);
return root;
}

private void swapChildren(TreeNode root){
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
}
}

101、对称二叉树

题目

给你一个二叉树的根节点 root , 检查它是否轴对称。

链接:https://leetcode.cn/problems/symmetric-tree/

示例

示例 1:

img

1
2
输入:root = [1,2,2,3,4,4,3]
输出:true

示例 2:

img

1
2
输入:root = [1,2,2,null,3,null,3]
输出: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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 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 boolean isSymmetric(TreeNode root) {
return compare(root.left, root.right);
}

private boolean compare(TreeNode left, TreeNode right) {
if (left == null && right != null) {
return false;
}
if (left != null && right == null) {
return false;
}
if (left == null && right == null) {
return true;
}
if (left.val != right.val) {
return false;
}
boolean compareOuside = compare(left.left, right.right);
boolean compareInside = compare(left.right, right.left);
return compareOuside && compareInside;
}
}

第十六天 | 力扣 104 & 111 & 222

104、二叉树的最大深度

题目

给定一个二叉树 root ,返回其最大深度。

二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。

链接:https://leetcode.cn/problems/maximum-depth-of-binary-tree/

示例

示例 1:

img

1
2
输入:root = [3,9,20,null,null,15,7]
输出:3

示例 2:

1
2
输入:root = [1,null,2]
输出: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
/**
* 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 int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
return Math.max(leftDepth, rightDepth) + 1;
}
}

111、二叉树的最小深度

题目

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

**说明:**叶子节点是指没有子节点的节点。

链接:https://leetcode.cn/problems/minimum-depth-of-binary-tree/

示例

示例 1:

img

1
2
输入:root = [3,9,20,null,null,15,7]
输出:2

示例 2:

1
2
输入:root = [2,null,3,null,4,null,5,null,6]
输出:5

题解

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
/**
* 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 int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
int leftDepth = minDepth(root.left);
int rightDepth = minDepth(root.right);
if (root.left == null) {
return rightDepth + 1;
}
if (root.right == null) {
return leftDepth + 1;
}
return Math.min(leftDepth, rightDepth) + 1;
}
}

222、完全二叉树的节点个数

题目

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。

完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

链接:https://leetcode.cn/problems/count-complete-tree-nodes/

示例

示例 1:

img

1
2
输入:root = [1,2,3,4,5,6]
输出:6

示例 2:

1
2
输入:root = []
输出:0

示例 3:

1
2
输入:root = [1]
输出:1

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 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 int countNodes(TreeNode root) {
if (root == null) {
return 0;
}
return countNodes(root.left) + countNodes(root.right) + 1;
}
}

第十七天 | 力扣 110 & 257 & 404

110、平衡二叉树

题目

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

链接:https://leetcode.cn/problems/balanced-binary-tree/

示例

示例 1:

img

1
2
输入:root = [3,9,20,null,null,15,7]
输出:true

示例 2:

img

1
2
输入:root = [1,2,2,3,3,null,null,4,4]
输出:false

示例 3:

1
2
输入:root = []
输出: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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 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 boolean isBalanced(TreeNode root) {
return getHeight(root) != -1;
}

private int getHeight(TreeNode root) {
if (root == null) {
return 0;
}
int leftHeight = getHeight(root.left);
if (leftHeight == -1) {
return -1;
}
int rightHeight = getHeight(root.right);
if (rightHeight == -1) {
return -1;
}
if (Math.abs(leftHeight - rightHeight) > 1) {
return -1;
}
return Math.max(leftHeight, rightHeight) + 1;
}
}

257、二叉树的所有路径

题目

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。

叶子节点 是指没有子节点的节点。

链接:https://leetcode.cn/problems/binary-tree-paths/

示例

示例 1:

img

1
2
输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]

示例 2:

1
2
输入:root = [1]
输出:["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
51
52
53
/**
* 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<String> binaryTreePaths(TreeNode root) {
List<String> res = new ArrayList<>();// 存最终的结果
if (root == null) {
return res;
}
List<Integer> paths = new ArrayList<>();// 作为结果中的路径
traversal(root, paths, res);
return res;
}

private void traversal(TreeNode root, List<Integer> paths, List<String> res) {
paths.add(root.val);// 前序遍历,中
// 遇到叶子结点
if (root.left == null && root.right == null) {
// 输出
StringBuilder sb = new StringBuilder();// StringBuilder用来拼接字符串,速度更快
for (int i = 0; i < paths.size() - 1; i++) {
sb.append(paths.get(i)).append("->");
}
sb.append(paths.get(paths.size() - 1));// 记录最后一个节点
res.add(sb.toString());// 收集一个路径
return;
}
// 递归和回溯是同时进行,所以要放在同一个花括号里
if (root.left != null) { // 左
traversal(root.left, paths, res);
paths.remove(paths.size() - 1);// 回溯
}
if (root.right != null) { // 右
traversal(root.right, paths, res);
paths.remove(paths.size() - 1);// 回溯
}
}
}

404、左叶子之和

题目

给定二叉树的根节点 root ,返回所有左叶子之和。

链接:https://leetcode.cn/problems/sum-of-left-leaves/

示例

示例 1:

img

1
2
3
输入: root = [3,9,20,null,null,15,7] 
输出: 24
解释: 在这个二叉树中,有两个左叶子,分别是 915,所以返回 24

示例 2:

1
2
输入: root = [1]
输出: 0

题解

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
/**
* 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 int sumOfLeftLeaves(TreeNode root) {
if (root == null) return 0;
int leftValue = sumOfLeftLeaves(root.left); // 左
int rightValue = sumOfLeftLeaves(root.right); // 右

int midValue = 0;
if (root.left != null && root.left.left == null && root.left.right == null) {
midValue = root.left.val;
}
int sum = midValue + leftValue + rightValue; // 中
return sum;
}
}

第十八天 | 力扣 513 & 112 & 106

513、找树左下角的值

题目

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

假设二叉树中至少有一个节点。

链接:https://leetcode.cn/problems/find-bottom-left-tree-value/

示例

示例 1:

img

1
2
输入: root = [2,1,3]
输出: 1

示例 2:

img

1
2
输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7

题解

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
/**
* 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 {
private int Deep = -1;
private int value = 0;
public int findBottomLeftValue(TreeNode root) {
value = root.val;
findLeftValue(root,0);
return value;
}

private void findLeftValue (TreeNode root,int deep) {
if (root == null) return;
if (root.left == null && root.right == null) {
if (deep > Deep) {
value = root.val;
Deep = deep;
}
}
if (root.left != null) findLeftValue(root.left,deep + 1);
if (root.right != null) findLeftValue(root.right,deep + 1);
}
}

112、路径总和

题目

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false

叶子节点 是指没有子节点的节点。

链接:https://leetcode.cn/problems/path-sum/

示例

示例 1:

img

1
2
3
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。

示例 2:

img

1
2
3
4
5
6
输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。

示例 3:

1
2
3
输入:root = [], targetSum = 0
输出: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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* 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 boolean hasPathSum(TreeNode root, int targetSum) {
if (root == null) {
return false;
}
targetSum -= root.val;
// 叶子结点
if (root.left == null && root.right == null) {
return targetSum == 0;
}
if (root.left != null) {
boolean left = hasPathSum(root.left, targetSum);
if (left) { // 已经找到
return true;
}
}
if (root.right != null) {
boolean right = hasPathSum(root.right, targetSum);
if (right) { // 已经找到
return true;
}
}
return false;
}
}

106、从中序与后序遍历序列构造二叉树

题目

给定两个整数数组 inorderpostorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树

链接:https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/

示例

示例 1:

img

1
2
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]

示例 2:

1
2
输入:inorder = [-1], postorder = [-1]
输出:[-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
/**
* 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 {
Map<Integer, Integer> map; // 方便根据数值查找位置
public TreeNode buildTree(int[] inorder, int[] postorder) {
map = new HashMap<>();
for (int i = 0; i < inorder.length; i++) { // 用map保存中序序列的数值对应位置
map.put(inorder[i], i);
}

return findNode(inorder, 0, inorder.length, postorder,0, postorder.length); // 前闭后开
}

public TreeNode findNode(int[] inorder, int inBegin, int inEnd, int[] postorder, int postBegin, int postEnd) {
// 参数里的范围都是前闭后开
if (inBegin >= inEnd || postBegin >= postEnd) { // 不满足左闭右开,说明没有元素,返回空树
return null;
}
int rootIndex = map.get(postorder[postEnd - 1]); // 找到后序遍历的最后一个元素在中序遍历中的位置
TreeNode root = new TreeNode(inorder[rootIndex]); // 构造结点
int lenOfLeft = rootIndex - inBegin; // 保存中序左子树个数,用来确定后序数列的个数
root.left = findNode(inorder, inBegin, rootIndex,
postorder, postBegin, postBegin + lenOfLeft);
root.right = findNode(inorder, rootIndex + 1, inEnd,
postorder, postBegin + lenOfLeft, postEnd - 1);

return root;
}
}

第十九天 | 力扣 654 & 617 & 700 & 98

654、最大二叉树

题目

给定一个不重复的整数数组 nums最大二叉树 可以用下面的算法从 nums 递归地构建:

  1. 创建一个根节点,其值为 nums 中的最大值。
  2. 递归地在最大值 左边子数组前缀上 构建左子树。
  3. 递归地在最大值 右边子数组后缀上 构建右子树。

返回 nums 构建的 最大二叉树

链接:https://leetcode.cn/problems/maximum-binary-tree/

示例

示例 1:

img

1
2
3
4
5
6
7
8
9
10
11
12
输入:nums = [3,2,1,6,0,5]
输出:[6,3,5,null,2,0,null,null,1]
解释:递归调用如下所示:
- [3,2,1,6,0,5] 中的最大值是 6 ,左边部分是 [3,2,1] ,右边部分是 [0,5] 。
- [3,2,1] 中的最大值是 3 ,左边部分是 [] ,右边部分是 [2,1] 。
- 空数组,无子节点。
- [2,1] 中的最大值是 2 ,左边部分是 [] ,右边部分是 [1] 。
- 空数组,无子节点。
- 只有一个元素,所以子节点是一个值为 1 的节点。
- [0,5] 中的最大值是 5 ,左边部分是 [0] ,右边部分是 [] 。
- 只有一个元素,所以子节点是一个值为 0 的节点。
- 空数组,无子节点。

示例 2:

img

1
2
输入:nums = [3,2,1]
输出:[3,null,2,null,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
/**
* 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 TreeNode constructMaximumBinaryTree(int[] nums) {
return constructMaximumBinaryTree1(nums, 0, nums.length);
}

public TreeNode constructMaximumBinaryTree1(int[] nums, int leftIndex, int rightIndex) {
if (rightIndex - leftIndex < 1) {// 没有元素了
return null;
}
if (rightIndex - leftIndex == 1) {// 只有一个元素
return new TreeNode(nums[leftIndex]);
}
int maxIndex = leftIndex;// 最大值所在位置
int maxVal = nums[maxIndex];// 最大值
for (int i = leftIndex + 1; i < rightIndex; i++) {
if (nums[i] > maxVal){
maxVal = nums[i];
maxIndex = i;
}
}
TreeNode root = new TreeNode(maxVal);
// 根据maxIndex划分左右子树
root.left = constructMaximumBinaryTree1(nums, leftIndex, maxIndex);
root.right = constructMaximumBinaryTree1(nums, maxIndex + 1, rightIndex);
return root;
}
}

617、合并二叉树

题目

给你两棵二叉树: root1root2

想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。

返回合并后的二叉树。

注意: 合并过程必须从两个树的根节点开始。

链接:https://leetcode.cn/problems/merge-two-binary-trees/

示例

示例 1:

img

1
2
输入:root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]
输出:[3,4,5,5,4,null,7]

示例 2:

1
2
输入:root1 = [1], root2 = [1,2]
输出:[2,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
/**
* 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 TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) return root2;
if (root2 == null) return root1;

root1.val += root2.val;
root1.left = mergeTrees(root1.left,root2.left);
root1.right = mergeTrees(root1.right,root2.right);
return root1;
}
}

700、二叉搜索树中的搜索

题目

给定二叉搜索树(BST)的根节点 root 和一个整数值 val

你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null

链接:https://leetcode.cn/problems/search-in-a-binary-search-tree/

示例

示例 1:

img

1
2
输入:root = [4,2,7,1,3], val = 2
输出:[2,1,3]

示例 2:

img

1
2
输入:root = [4,2,7,1,3], val = 5
输出:[]

题解

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
/**
* 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 TreeNode searchBST(TreeNode root, int val) {
if (root == null || root.val == val) {
return root;
}
TreeNode left = searchBST(root.left, val);
if (left != null) {
return left;
}
return searchBST(root.right, val);
}
}

98、验证二叉搜索树

题目

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

链接:https://leetcode.cn/problems/validate-binary-search-tree/

示例

示例 1:

img

1
2
输入:root = [2,1,3]
输出:truejava

示例 2:

img

1
2
3
输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4

题解

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
/**
* 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 boolean isValidBST(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode pre = null;
if(root != null)
stack.add(root);
while(!stack.isEmpty()){
TreeNode curr = stack.peek();
if(curr != null){
stack.pop();
if(curr.right != null)
stack.add(curr.right);
stack.add(curr);
stack.add(null);
if(curr.left != null)
stack.add(curr.left);
}else{
stack.pop();
TreeNode temp = stack.pop();
if(pre != null && pre.val >= temp.val)
return false;
pre = temp;
}
}
return true;
}
}

LeetCode
https://www.renkelin.vip/2024/02/17/LeetCode/
Author
Kolin
Posted on
February 17, 2024
Licensed under