代码随想录

704. 二分查找

题目

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

示例

示例 1

输入:nums = [-1,0,3,5,9,12], target = 9

输出:4

解释:9 出现在 nums 中并且下标为 4

示例 2

输入:nums = [-1,0,3,5,9,12], target = 2

输出:-1

解释:2 不存在 nums 中因此返回 -1

提示

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

提交历史

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

if (nums[mid] < target) {
left = mid + 1;
}else {
right = mid;
}
}

if (nums[left] == target)
return left;
else
return -1;
}
}

题解

数组为有序数组,且数组中无重复元素,考虑使用二分法。

如果target是在一个左闭右开的区间里,那么循环条件while(left <= right),因为因为left == right是有意义的,所以使用 <=

如果target是在一个在左闭右开的区间里,while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的。在更新新区间时也要考虑开闭区间的问题。

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

复杂度分析

  • 时间复杂度:O(logN)。N是数组中的元素数量
  • 空间复杂度:O(1)

27. 移除元素

题目

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

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

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

示例

示例 1

输入: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

输入: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。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

提示

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100

提交历史

因为题目要求不考虑数组中超出新长度后面的元素,所以只要保证在删除后的新长度内没有等于val值的元素即可。使用双指针法,左指针指向数组开头,右指针指向数组结尾。先把左指针右移找到第一个等于val即要移动到后面的数,再把右指针左移找到第一个不等于val即要移动到前面的数,接下来交换位置或覆盖(我使用了交换位置)。以此类推,最终在新长度内就不存在val

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 removeElement(int[] nums, int val) {
int len = nums.length;
int newlen = 0;
int left = 0;
int right = len - 1;
for (int i = 0; i < len; i ++) {
if (nums[i] != val)
newlen ++;
}

while (left < right) {
while (left < right && nums[left] != val)
left ++;

while (left < right && nums[right] == val)
right --;

if (left < right) {
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
left ++;
right --;
}
}

return newlen;
}
}

题解

因为数组的元素在内存地址中是连续的,所以不能单独删除数组中的某个元素,只能覆盖。

方法一

暴力解法,两层for循环,第一层遍历数组元素,第二层循环更新数组。把要删除的元素后面的所有元素依次往前挪一个覆盖。

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

复杂度分析

  • 时间复杂度:O(N^2)。N是数组中的元素数量
  • 空间复杂度:O(1)

方法二

双指针法(快慢指针)。快指针指向当前将要处理的元素(要被舍弃/要输出),慢指针指向当前将要处理的位置。如果快指针指向的数是要求删除的数我们就右移快指针而慢指针不变,如果是要包含在输出里的数就把他覆盖在当前慢指针的位置上,并把快慢指针同时右移。优点是可以保证元素的相对位置

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

return slowIndex;
}
}

复杂度分析

  • 时间复杂度:O(N)。N是数组中的元素数量
  • 空间复杂度:O(1)

方法三

双指针法(左右指针)。与我的方法类似。优点是避免了需要保留的元素的重复赋值操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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];
right--;
}
left++;
while(right >= 0 && nums[right] == val) right--;
}
return left;
}
}

复杂度分析

  • 时间复杂度:O(N)。N是数组中的元素数量
  • 空间复杂度:O(1)

977. 有序数组的平方

题目

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

示例

示例 1

输入:nums = [-4,-1,0,3,10]

输出:[0,1,9,16,100]

解释:平方后,数组变为 [16,1,0,9,100]。排序后,数组变为 [0,1,9,16,100]

示例 2

输入:nums = [-7,-3,2,3,11]

输出:[4,9,9,49,121]

提示

  • 1 <= nums.length <= 10^4
  • -10^4 <= nums[i] <= 10^4
  • nums 已按 非递减顺序 排序

进阶

请你设计时间复杂度为 O(n) 的算法解决本问题

提交历史

第一次提交

暴力解,先把数组里的所有元素都平方,然后直接使用Arrays.sort排序。

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

复杂度分析

  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(logn)

第二次提交(进阶)

双指针。因为原数组是包含负数从小到大排列的,所以每个元素平方之后最大的数一定出现在两端中的一个元素处。所以我们设置一个左指针一个右指针分别指向当前数组的左端和右端,再创建一个长度等于原数组的新数组用来存储平方之后的元素。如果左指针指向的数的平方更大,那么就在新数组的最后一个位置存储这个数的平方,再把左指针向右移动一位,下一步比较右移后的左指针指向的数和右指针指向的数的平方。以此类推,每比较一次左指针或右指针移动一次,同时新数组从后向前存一个平方数。

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

题解

同进阶中方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int[] sortedSquares(int[] nums) {
int right = nums.length - 1;
int left = 0;
int[] result = new int[nums.length];
int index = result.length - 1;
while (left <= right) {
if (nums[left] * nums[left] > nums[right] * nums[right]) {
// 正数的相对位置是不变的, 需要调整的是负数平方后的相对位置
result[index--] = nums[left] * nums[left];
++left;
} else {
result[index--] = nums[right] * nums[right];
--right;
}
}
return result;
}
}

复杂度分析

  • 时间复杂度:O(n)。
  • 空间复杂度:O(n)。

209.长度最小的子数组

题目

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

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

示例

示例 1

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

示例 2

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

示例 3

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

提示

  • 1 <= target <= 10^9
  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^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
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int sum = 0;
for (int i = 0; i < nums.length; i ++) {
sum += nums[i];
}
if (sum < target)
return 0;
else {
int minlen = nums.length;

for (int j = 0; j < nums.length; j ++) {
int currentsum = 0;
int currentlen = 0;
for (int k = j; k < nums.length; k ++) {
currentsum += nums[k];
currentlen ++;

if ((currentsum >= target) && (currentlen < minlen))
minlen = currentlen;
}
}

return minlen;
}
}
}

第二次提交(超时)

修改了循环条件,减少了循环次数。依旧超时

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 int minSubArrayLen(int target, int[] nums) {
int sum = 0;
for (int i = 0; i < nums.length; i ++) {
sum += nums[i];
}
if (sum < target)
return 0;
else {
int minlen = nums.length;

for (int j = 0; j < nums.length; j ++) {
int currentsum = 0;
int currentlen = 0;
for (int k = j, l = 1; (l <= minlen) && (k < nums.length) ; k ++, l ++) {
currentsum += nums[k];
currentlen ++;

if ((currentsum >= target) && (currentlen < minlen))
minlen = currentlen;
}
}

return minlen;
}
}
}

第三次提交(过)

双指针法:

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 minSubArrayLen(int target, int[] nums) {
int s = 0;
for (int i = 0; i < nums.length; i ++) {
s += nums[i];
}
if (s < target)
return 0;
else {
int left = 0;
int minlen = nums.length;
int sum = 0;

for (int right = 0; right < nums.length; right ++) {
sum += nums[right];
while (sum >= target) {
minlen = Math.min(minlen, (right - left + 1));
sum -= nums[left];
left ++;
}
}

return minlen;
}
}
}

题解

滑动窗口法

滑动窗口就是不断调整子序列的起始位置和终止位置,得到想要的结果。相比暴力双循环解法,只需要一个for循环就可以完成搜索整个数组的操作。滑动窗口循环的索引一定是表示滑动窗口的终止位置。

在这道题中,窗口就是满足其和 ≥ target 的长度最小的 连续 子数组,窗口的终止位置就是遍历数组的指针,也就是for循环的索引,窗口的起始位置就是左指针,如果当前窗口中的值大于target了就该把左指针向右移动再次判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
// 滑动窗口
public int minSubArrayLen(int s, 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 >= s) {
result = Math.min(result, right - left + 1);
sum -= nums[left++];
}
}
return result == Integer.MAX_VALUE ? 0 : result;
}
}

复杂度分析

  • 时间复杂度:O(n)。
  • 空间复杂度:O(1)

59.螺旋矩阵2

题目

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

示例

示例 1

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

示例 2

输入:n = 1
输出:[[1]]

提示

  • 1 <= n <= 20

提交历史

第一次提交(错)

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 int[][] generateMatrix(int n) {
int res[][] = new int[n][n];
int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
int x = 0, y = 0, d = 0;

for (int i = 0; i < n * n; i ++) {
res[x][y] = i;
int a = x + dx[d], b = y + dy[d];
if (a < 0 || a >= n || b < 0 || b >= n || res[a][b] > 0) {
d ++;
d %= 4;
a = x + dx[x];
b = y + dx[y];
}
x = a;
y = b;
}

return res;
}
}

第二次提交(过)

修改了循环条件,减少了循环次数。依旧超时

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 int[][] generateMatrix(int n) {
int res[][] = new int[n][n];
int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
int x = 0, y = 0, d = 0;

for (int i = 1; i <= n * n; i ++) {
res[x][y] = i;
int a = x + dx[d], b = y + dy[d];
if (a < 0 || a >= n || b < 0 || b >= n || res[a][b] > 0)
{
d = (d + 1) % 4;
a = x + dx[d];
b = y + dy[d];
}
x = a;
y = b;
}

return res;
}
}

题解

力扣官方题解

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[][] generateMatrix(int n) {
int maxNum = n * n;
int curNum = 1;
int[][] matrix = new int[n][n];
int row = 0, column = 0;
int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; // 右下左上
int directionIndex = 0;
while (curNum <= maxNum) {
matrix[row][column] = curNum;
curNum++;
int nextRow = row + directions[directionIndex][0], nextColumn = column + directions[directionIndex][1];
if (nextRow < 0 || nextRow >= n || nextColumn < 0 || nextColumn >= n || matrix[nextRow][nextColumn] != 0) {
directionIndex = (directionIndex + 1) % 4; // 顺时针旋转至下一个方向
}
row = row + directions[directionIndex][0];
column = column + directions[directionIndex][1];
}
return matrix;
}
}

复杂度分析

  • 时间复杂度:O(n^2)。
  • 空间复杂度:O(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
class Solution {
public int[][] generateMatrix(int n) {
int loop = 0; // 控制循环次数
int[][] res = new int[n][n];
int start = 0; // 每次循环的开始点(start, start)
int count = 1; // 定义填充数字
int i, j;

while (loop++ < n / 2) { // 判断边界后,loop从1开始
// 模拟上侧从左到右
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.移除链表元素

题目

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

示例

示例 1

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

示例 2

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

示例 3

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

提示

  • 列表中的节点数目在范围 [0, 104] 内
  • 1 <= Node.val <= 50
  • 0 <= val <= 50

提交历史

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 removeElements(ListNode head, int val) {
ListNode dummy = new ListNode(-1, head);
ListNode pre = dummy;
ListNode cur = head;
while (cur != null) {
if (cur.val == val) {
pre.next = cur.next;
}
else {
pre = cur;
}
cur = cur.next;
}
return dummy.next;
}
}

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode temp = dummyHead;
while (temp.next != null) {
if (temp.next.val == val) {
temp.next = temp.next.next;
} else {
temp = temp.next;
}
}
return dummyHead.next;
}
}

复杂度分析

  • 时间复杂度:O(n)。
  • 空间复杂度:O(1)。

707. 设计链表

题目c

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

单链表中的节点应该具备两个属性: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 的节点。

示例

示例

示例 1

输入:[“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

提示

  • 0 <= index, val <= 1000
  • 不要使用内置的 LinkedList 库。
  • 调用 getaddAtHeadaddAtTailaddAtIndexdeleteAtIndex 的次数不超过 2000 。

提交历史

第一次提交(错)

循环条件搞错了

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
class MyLinkedList {

int size;
ListNode head;

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

public int get(int index) {
if (index < 0 || index >= size) {
return -1;
}
ListNode currentNode = head;
for (int i = 0; i <= index; i ++) {
currentNode = currentNode.next;
}
return currentNode.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 || index < 0) return;
size ++;
ListNode add = new ListNode(val);
ListNode prev = head;
for (int i = 0; i < index; i ++) {
prev = prev.next;
}
add.next = prev.next;
prev.next = add;
}

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

}
}

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

第二次提交(过)

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
class MyLinkedList {

int size;
ListNode head;

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

public int get(int index) {
if (index < 0 || index >= size) {
return -1;
}
ListNode currentNode = head;
for (int i = 0; i <= index; i ++) {
currentNode = currentNode.next;
}
return currentNode.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 || index < 0) return;
size ++;
ListNode add = new ListNode(val);
ListNode pred = head;
for (int i = 0; i < index; i ++) {
pred = pred.next;
}
add.next = pred.next;
pred.next = add;
}

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

}
}

class ListNode {
int val;
ListNode next;

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

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

题解

单链表

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
class MyLinkedList {
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;
}
index = Math.max(0, index);
size++;
ListNode pred = head;
for (int i = 0; i < index; i++) {
pred = pred.next;
}
ListNode toAdd = new ListNode(val);
toAdd.next = pred.next;
pred.next = toAdd;
}

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

class ListNode {
int val;
ListNode next;

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

双链表

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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
class MyLinkedList {

int size;
ListNode head;
ListNode tail;

public MyLinkedList() {
this.size = 0;
this.head = new ListNode(0);
this.tail = new ListNode(0);
head.next = tail;
tail.prev = head;
}

public int get(int index) {
if (index < 0 || index >= size)
return -1;
ListNode currentNode;
if (index + 1 < size - index) {
currentNode = head;
for (int i = 0; i <= index; i ++) {
currentNode = currentNode.next;
}
}
else {
currentNode = tail;
for (int i = 0; i < size - index; i ++) {
currentNode = currentNode.prev;
}
}
return currentNode.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;

index = Math.max(0, index);

ListNode pred, succ;

if(index < size - index) {
pred = head;
for (int i = 0; i < index; i ++) {
pred = pred.next;
}
succ = pred.next;
}
else {
succ = tail;
for (int i = 0; i < size - index; i ++) {
succ = succ.prev;
}
pred = succ.prev;
}

size ++;
ListNode addNode = new ListNode(val);

addNode.prev = pred;
addNode.next = succ;
pred.next = addNode;
succ.prev = addNode;
}

public void deleteAtIndex(int index) {
if (index < 0 || index >= size)
return;

ListNode pred, succ;

if(index < size - index) {
pred = head;
for (int i = 0; i < index; i ++) {
pred = pred.next;
}
succ = pred.next.next;
}
else {
succ = tail;
for (int i = 0; i < size - index - 1; i ++) {
succ = succ.prev;
}
pred = succ.prev.prev;
}

pred.next = succ;
succ.prev = pred;
size --;
}
}


class ListNode {
int val;
ListNode next;
ListNode prev;

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


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

复杂度分析

  • 时间复杂度:O(logN)。N是数组中的元素数量
  • 空间复杂度:O(1)

707. 反转链表

题目

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

示例

示例

示例 1

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

示例 2

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

示例 3

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

提示c

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

提交历史

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 reverseList(ListNode head) {
ListNode curr = head;
ListNode prev = null;
ListNode tmp = null;

while(curr != null) {
tmp = curr.next;
curr.next = prev;
prev = curr;
curr = tmp;
}

return prev;
}
}

题解

双指针法(迭代法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode cur = head;
ListNode temp = null;
while (cur != null) {
temp = cur.next;// 保存下一个节点
cur.next = prev;
prev = cur;
cur = temp;
}
return prev;
}
}

递归法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 = null;
temp = cur.next;// 先保存下一个节点
cur.next = prev;// 反转
// 更新prev、cur位置
// prev = cur;
// cur = temp;
return reverse(cur, temp);
}
}

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

题目

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

示例

示例

示例 1

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

示例 2

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

示例 3

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

提示c

  • 链表中节点的数目范围是 [0, 100]
  • -5000 <= Node.val <= 100

提交历史

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
/**
* 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 dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode currentNode = dummyNode;
ListNode frontNode;
ListNode behindNode;
ListNode tmp;
while (currentNode.next != null && currentNode.next.next != null) {
tmp = currentNode.next.next.next;
frontNode = currentNode.next;
behindNode = currentNode.next.next;

currentNode.next = behindNode;
behindNode.next = frontNode;
frontNode.next = tmp;

currentNode = frontNode;
}

return dummyNode.next;
}
}

题解

双指针法(迭代法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dumyhead = new ListNode(-1); // 设置一个虚拟头结点
dumyhead.next = head; // 将虚拟头结点指向head,这样方便后面做删除操作
ListNode cur = dumyhead;
ListNode temp; // 临时节点,保存两个节点后面的节点
ListNode firstnode; // 临时节点,保存两个节点之中的第一个节点
ListNode secondnode; // 临时节点,保存两个节点之中的第二个节点
while (cur.next != null && cur.next.next != null) {
temp = cur.next.next.next;
firstnode = cur.next;
secondnode = cur.next.next;
cur.next = secondnode; // 步骤一
secondnode.next = firstnode; // 步骤二
firstnode.next = temp; // 步骤三
cur = firstnode; // cur移动,准备下一轮交换
}
return dumyhead.next;
}
}

递归法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public ListNode swapPairs(ListNode head) {
// base case 退出提交
if(head == null || head.next == null) return head;
// 获取当前节点的下一个节点
ListNode next = head.next;
// 进行递归
ListNode newNode = swapPairs(next.next);
// 这里进行交换
next.next = head;
head.next = newNode;

return next;
}
}

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

题目

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

示例

示例

示例 1

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

示例 2

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

示例 3

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

提示c

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

提交历史

第一次提交(错)

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
/**
* 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 dummyNode = new ListNode(-1);
ListNode currentNode = dummyNode;
dummyNode.next = head;
int size = 0;

while (currentNode.next != null) {
size ++;
currentNode = currentNode.next;
}

currentNode = dummyNode;
for (int i = 0; i < size - n; i ++) {
currentNode = currentNode.next;
}

if (currentNode.next.next != null) {
currentNode.next = currentNode.next.next;
}
else {
currentNode.next = null;
}

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
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* 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 dummyNode = new ListNode(-1);
ListNode currentNode = dummyNode;
dummyNode.next = head;
int size = 0;

while (currentNode.next != null) {
size ++;
currentNode = currentNode.next;
}

if (size == 1) return null;

currentNode = dummyNode;
for (int i = 0; i < size - n; i ++) {
currentNode = currentNode.next;
}

if (currentNode.next.next != null) {
currentNode.next = currentNode.next.next;
}
else {
currentNode.next = null;
}

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
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* 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 dummyNode = new ListNode(-1);
ListNode currentNode = dummyNode;
dummyNode.next = head;
int size = 0;

while (currentNode.next != null) {
size ++;
currentNode = currentNode.next;
}

if (size == 1) return null;

currentNode = dummyNode;
for (int i = 0; i < size - n; i ++) {
currentNode = currentNode.next;
}

if (currentNode.next.next != null) {
currentNode.next = currentNode.next.next;
}
else {
currentNode.next = null;
}

return dummyNode.next;
}
}

题解

双指针法

定义双指针,先让快指针向后移动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
30
31
/**
* 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 dummy = new ListNode(-1);
dummy.next = head;
ListNode fast = dummy;
ListNode slow = dummy;

for (int i = 0; i < n; i ++) {
fast = fast.next;
}

while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}

slow.next = slow.next.next;

return dummy.next;
}
}

160. 链表相交

题目

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

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

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

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

示例

示例

示例 1
example1

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5],skipA = 2, skipB = 3
输出:Intersected at ‘8’

示例 2
example2

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at ‘2’

示例 3
example3

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null

提示c

  • listA 中节点数目为 m
  • listB 中节点数目为 n
  • 0 <= m, n <= 3 * 104
  • 1 <= Node.val <= 105
  • 0 <= skipA <= m
  • 0 <= skipB <= n
  • 如果 listAlistB 没有交点,intersectVal 为 0
  • 如果 listAlistB 有交点,intersectVal == listA[skipA + 1] == listB[skipB + 1]

进阶

你能否设计一个时间复杂度 O(n) 、仅用 O(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
/**
* 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 currA = headA;
ListNode currB = headB;

while (currA != null) {
while (currB != null) {
if (currA == currB) {
return currA;
}
else
currB = currB.next;
}
currB = headB;
currA = currA.next;
}

return 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
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB;
int lenA = 0, lenB = 0;
while (curA != null) { // 求链表A的长度
lenA++;
curA = curA.next;
}
while (curB != null) { // 求链表B的长度
lenB++;
curB = curB.next;
}
curA = headA;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
//1. swap (lenA, lenB);
int tmpLen = lenA;
lenA = lenB;
lenB = tmpLen;
//2. swap (curA, curB);
ListNode tmpNode = curA;
curA = curB;
curB = tmpNode;
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap-- > 0) {
curA = curA.next;
}
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != null) {
if (curA == curB) {
return curA;
}
curA = curA.next;
curB = curB.next;
}
return null;
}
}

复杂度分析

  • 时间复杂度:O(n + m)
  • 空间复杂度:O(1)

124. 环形链表 II

题目

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

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

示例

示例

示例 1
example1

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

示例 2
example1

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

示例 3
example1

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环

提示c

  • 链表中节点的数目范围在范围 [0, 10^4]
  • -10^5 <= Node.val <= 10^5
  • pos 的值为 -1 或者链表中的一个有效索引

进阶

是否可以使用 O(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
/**
* 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) {
Set<ListNode> set = new HashSet<ListNode>();
ListNode cur = head;
while (cur != null) {
if (set.contains(cur))
return cur;
else
set.add(cur);

cur = cur.next;
}
return null;
}
}

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

242. 有效的字母异位词

题目

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

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

示例

示例

示例 1

输入:s = “anagram”, t = “nagaram”
输出:true

示例 2

输入:s = “rat”, t = “car”
输出:false

提示c

  • 1 <= s.length, t.length <= 5 * 10^4
  • s 和 t 仅包含小写字母

提交历史

第一次提交(错)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public boolean isAnagram(String s, String t) {
int[] cntS = new int[100];
int[] cntT = new int[100];
char[] charS = s.toCharArray();
char[] charT = t.toCharArray();

for (int i = 0; i < s.length(); i ++) {
cntS[charS[i] - 'A'] ++;
cntT[charT[i] - 'A'] ++;
}

for (int j = 0; j < 100; j ++) {
if (cntS[j] != cntT[j])
return false;
}

return true;
}
}

第二次提交(过)

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 boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}

int[] cntS = new int[100];
int[] cntT = new int[100];

for (int i = 0; i < s.length(); i++) {
cntS[s.charAt(i) - 'A']++;
cntT[t.charAt(i) - 'A']++;
}

for (int j = 0; j < 100; j++) {
if (cntS[j] != cntT[j]) {
return false;
}
}

return true;
}
}

题解

排序

ts的异位词等价于二者排序后相等,可以先转换成字符数组后排序

1
2
3
4
5
6
7
8
9
class Solution {
public boolean isAnagram(String s, String t) {
char[] strS = s.toCharArray();
char[] strT = t.toCharArray();
Arrays.sort(strS);
Arrays.sort(strT);
return Arrays.equals(strS, strT);
}
}

复杂度分析

  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(n)

计数数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public boolean isAnagram(String s, String t) {
int[] record = new int[26];

for (int i = 0; i < s.length(); i++) {
record[s.charAt(i) - 'a']++; // 并不需要记住字符a的ASCII,只要求出一个相对数值就可以了
}

for (int i = 0; i < t.length(); i++) {
record[t.charAt(i) - 'a']--;
}

for (int count: record) {
if (count != 0) { // record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。
return false;
}
}
return true; // record数组所有元素都为零0,说明字符串s和t是字母异位词
}
}

复杂度分析

  • 时间复杂度:O(n + m)
  • 空间复杂度:O(1)

349. 两个数组的交集

题目

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

示例

示例

示例 1

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

示例 2

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

提示c

  • 1 <= nums1.length, nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 1000

提交历史

两个HashSet,第一个用于存nums1中的所有元素,然后遍历nums2,如果有相同的就加到第二个HashSet中然后返回。

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

for (int i = 0; i < nums1.length; i ++) {
element.add(nums1[i]);
}

for (int j = 0; j < nums2.length; j ++) {
if (element.contains(nums2[j])) {
ans.add(nums2[j]);
}
}

return ans.stream().mapToInt(Integer::intValue).toArray();
}
}

复杂度分析

  • 时间复杂度:O(n + m)
  • 空间复杂度:O(n + m)

202. 快乐数

题目

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

「快乐数」 定义为:

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

示例

示例

示例 1

输入:n = 19
输出:true
解释
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

示例 2

输入:n = 2
输出:false

提示c

  • 1 <= n <= 2^31 - 1

提交历史

关键在于题目所说的“无限循环”,也就是说可能某一次计算的sum结果会重复出现,就可以用哈希法来判断sum是否重复出现,如果发现重复了就返回false,否则一直继续到sum等于1。

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> set = new HashSet<Integer>();
while (n != 1 && !set.contains(n)) {
set.add(n);
n = func(n);
}
return n == 1;
}

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

题解

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> record = new HashSet<>();
while (n != 1 && !record.contains(n)) {
record.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;
}
}

复杂度分析

  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(nlogn)

1. 两数之和

题目

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

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

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

示例

示例 1

输入:nums = [2,7,11,15], target = 9

输出:[0,1]

解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1]

示例 2

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

输出:[1,2]

示例 3

输入:nums = [3,3], target = 6

输出:[0,1]

提示

  • 2 <= nums.length <= 10^4
  • -10^9 <= nums[i] <= 10^9
  • -10^9 <= target <= 10^9
  • 只会存在一个有效答案

提交历史

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] res = new int[2];

for (int i = 0; i < nums.length; i ++) {
for (int j = i + 1; j <nums.length; j ++) {
if (nums[i] + nums[j] == target) {
res[0] = i;
res[1] = j;
}
}
}

return res;
}
}

题解

方法一

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public int[] twoSum(int[] nums, int target) {
int n = nums.length;
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
if (nums[i] + nums[j] == target) {
return new int[]{i, j};
}
}
}
return new int[0];
}
}

改进

  1. int n = nums.length;将数组的长度存储在变量中,避免在每次进行循环时调用一次length
  2. 使用return new int[]{i, j};return new int[0];直接返回结果,相比int[] res = new int[2];避免使用额外的数组。

复杂度分析

  • 时间复杂度:O(N^2)。N是数组中的元素数量,最坏情况下数组中任意两个数都要被匹配一次
  • 空间复杂度:O(1)

方法二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>();
int len = nums.length;
for (int i = 0; i < len; i ++) {
if (hashtable.containsKey(target - nums[i])) {
return new int[]{hashtable.get(target - nums[i]), i};
}
hashtable.put(nums[i], i);
}

return new int[0];
}
}

复杂度分析

  • 时间复杂度:O(N)。
  • 空间复杂度:O(N)。

454. 四数相加 II

题目

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

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

示例

示例

示例 1

输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释
两个元组如下:

  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

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

提示c

  • n == nums1.length
  • n == nums2.length
  • n == nums3.length
  • n == nums4.length
  • 1 <= n <= 200
  • -2^28 <= nums1[i], nums2[i], nums3[i], nums4[i] <= 2^28

题解

  1. 首先定义一个mapkey存放a、b两数之和,value存放a、b两数之和出现的次数。
  2. 定义int变量count用来统计a + b + c + d == 0的次数
  3. 如果0 - (c + d)map中出现过就用countmapkey对应的value,也就是出现次数统计出来。
  4. 最后返回统计值 count
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
    int res = 0;
    Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    //统计两个数组中的元素之和,同时统计出现的次数,放入map
    for (int i : nums1) {
    for (int j : nums2) {
    int sum = i + j;
    map.put(sum, map.getOrDefault(sum, 0) + 1);
    }
    }
    //统计剩余的两个元素的和,在map中找是否存在相加为0的情况,同时记录次数
    for (int i : nums3) {
    for (int j : nums4) {
    res += map.getOrDefault(0 - i - j, 0);
    }
    }
    return res;
    }
    }
    复杂度分析
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(n^2)

383. 赎金信

题目

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

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

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

示例

示例

示例 1

输入:ransomNote = “a”, magazine = “b”
输出:false

示例 2

输入:ransomNote = “aa”, magazine = “ab”
输出:false

示例 3

输入:ransomNote = “aa”, magazine = “aab”
输出:true

提示c

  • 1 <= ransomNote.length, magazine.length <= 10^5
  • ransomNote 和 magazine 由小写英文字母组成

提交历史

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 boolean canConstruct(String ransomNote, String magazine) {
Map<Character, Integer> map = new HashMap<Character, Integer>();
for (int i = 0; i < ransomNote.length(); i ++) {
if (map.containsKey(ransomNote.charAt(i))) {
map.put(ransomNote.charAt(i), map.get(ransomNote.charAt(i)) + 1);
}
else {
map.put(ransomNote.charAt(i), 1);
}
}

for (int j = 0; j < magazine.length(); j ++) {
if (map.containsKey(magazine.charAt(j))) {
if (map.get(magazine.charAt(j)) > 1) {
map.put(magazine.charAt(j), map.get(magazine.charAt(j)) - 1);
}
else {
map.remove(magazine.charAt(j));
}
}
}

if (map.isEmpty())
return true;
else
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
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
// shortcut
if (ransomNote.length() > magazine.length()) {
return false;
}
// 定义一个哈希映射数组
int[] record = new int[26];

// 遍历
for(char c : magazine.toCharArray()){
record[c - 'a'] += 1;
}

for(char c : ransomNote.toCharArray()){
record[c - 'a'] -= 1;
}

// 如果数组中存在负数,说明ransomNote字符串总存在magazine中没有的字符
for(int i : record){
if(i < 0){
return false;
}
}

return true;
}
}

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

15. 三数之和

题目

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

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

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

示例

示例

示例 1

输入: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

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

示例 3

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

提示c

  • 3 <= nums.length <= 3000
  • -10^5 <= nums[i] <= 10^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
32
33
34
35
36
37
38
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
// 找出a + b + c = 0
// a = nums[i], b = nums[left], c = nums[right]
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]));
// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;

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

复杂度分析

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)

18. 四数之和

题目

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

  • 0 <= a, b, c, d < n
  • a、b、c 和 d 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target
    可以按 任意顺序 返回答案 。

示例

示例

示例 1

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

示例 2

输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]

提示c

  • 1 <= nums.length <= 200
  • -10^9 <= nums[i] <= 10^9
  • -10^9 <= target <= 10^9

题解

相比三数之和要注意循环条件和剪枝条件

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 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) {
// nums[k] + nums[i] + nums[left] + nums[right] > target int会溢出
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;
}
}

复杂度分析

  • 时间复杂度:O(n^3)
  • 空间复杂度:O(1)

344. 反转字符串

题目

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

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

示例

示例

示例 1

输入:s = [“h”,”e”,”l”,”l”,”o”]
输出:[“o”,”l”,”l”,”e”,”h”]

示例 2

输入:s = [“H”,”a”,”n”,”n”,”a”,”h”]
输出:[“h”,”a”,”n”,”n”,”a”,”H”]

提示c

  • 1 <= s.length <= 10^5
  • s[i] 都是 ASCII 码表中的可打印字符

提交记录

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

题解

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

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

541. 反转字符串 II

题目

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

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

示例

示例

示例 1

输入:s = “abcdefg”, k = 2
输出:”bacdfeg”

示例 2

输入:s = “abcd”, k = 2
输出:”bacd”

提示c

  • 1 <= s.length <= 10^4
  • s 仅由小写英文组成
  • 1 <= k <= 10^4

提交记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public String reverseStr(String s, int k) {
char[] charS = s.toCharArray();
for (int i = 0; i < charS.length; i += k * 2) {
int left = i;
int right = Math.min(left + k - 1, charS.length - 1);
while (left < right) {
char tmp = charS[left];
charS[left] = charS[right];
charS[right] = tmp;
right --;
left ++;
}
}
return new String(charS);
}
}

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public String reverseStr(String s, int k) {
char[] ch = s.toCharArray();
for(int i = 0;i < ch.length;i += 2 * k){
int start = i;
// 判断尾数够不够k个来取决end指针的位置
int end = Math.min(ch.length - 1,start + k - 1);
while(start < end){

char temp = ch[start];
ch[start] = ch[end];
ch[end] = temp;

start++;
end--;
}
}
return new String(ch);
}
}

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

54. 替换数字

题目

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

示例

示例

示例 1

输入:a1b2c3
输出:anumberbnumbercnumber

示例 2

输入:s = “abcd”, k = 2
输出:”bacd”

提示c

  • 1 <= s.length < 10000

提交记录

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

class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.next();
char[] ch = str.toCharArray();
for (int i = 0; i < ch.length; i ++) {
if (ch[i] >= '0' && ch[i] <= '9')
System.out.print("number");
else
System.out.print(ch[i]);
}
}
}

题解

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

复杂度分析

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)