代码随想录 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; } }
复杂度分析 :
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; } }
复杂度分析 :
59.螺旋矩阵2 题目 给你一个正整数 n
,生成一个包含 1
到 n^2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
示例 示例 1 :
输入 :n = 3输出 :[[1,2,3],[8,9,4],[7,6,5]]
示例 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 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 ; int count = 1 ; int i, j; 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.移除链表元素 题目 给你一个链表的头节点 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 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; } }
复杂度分析 :
707. 设计链表 题目c 你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val
和 next
。val
是当前节点的值,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 库。
调用 get
、addAtHead
、addAtTail
、addAtIndex
和 deleteAtIndex
的次数不超过 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 --; } }
第二次提交(过) 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; } }
题解 单链表 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; } }
复杂度分析 :
时间复杂度: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 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; 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 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; 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; } 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) { 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 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 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 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 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
开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
示例 示例 示例 1 :
输入 :intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5],skipA = 2, skipB = 3输出 :Intersected at ‘8’
示例 2 :
输入 :intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1输出 :Intersected at ‘2’
示例 3 :
输入 :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
如果 listA
和 listB
没有交点,intersectVal
为 0
如果 listA
和 listB
有交点,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 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 ) { lenA++; curA = curA.next; } while (curB != null ) { lenB++; curB = curB.next; } curA = headA; curB = headB; if (lenB > lenA) { int tmpLen = lenA; lenA = lenB; lenB = tmpLen; ListNode tmpNode = curA; curA = curB; curB = tmpNode; } 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 ; } }
复杂度分析 :
时间复杂度:O(n + m)
空间复杂度:O(1)
124. 环形链表 II 题目 给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
示例 示例 示例 1 :
输入 :head = [3,2,0,-4], pos = 1输出 :返回索引为 1 的链表节点解释 :链表中有一个环,其尾部连接到第二个节点
示例 2 :
输入 :head = [1,2], pos = 0输出 :返回索引为 0 的链表节点解释 :链表中有一个环,其尾部连接到第一个节点
示例 3 :
输入 :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 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 ; } }
复杂度分析 :
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 ; } }
题解 排序 t
是s
的异位词等价于二者排序后相等,可以先转换成字符数组后排序
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' ]++; } for (int i = 0 ; i < t.length(); i++) { record[t.charAt(i) - 'a' ]--; } for (int count: record) { if (count != 0 ) { return false ; } } return true ; } }
复杂度分析 :
时间复杂度:O(n + m)
空间复杂度:O(1)
349. 两个数组的交集 题目 给定两个数组 nums1
和 nums2
,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例 示例 示例 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
提交历史 关键在于题目所说的“无限循环”,也就是说可能某一次计算的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 ]; } }
改进 :
int n = nums.length;
将数组的长度存储在变量中,避免在每次进行循环时调用一次length
。
使用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 ]; } }
复杂度分析 :
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解释 : 两个元组如下:
(0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
(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
题解
首先定义一个map
,key
存放a、b两数之和,value
存放a、b两数之和出现的次数。
定义int
变量count
用来统计a + b + c + d == 0
的次数
如果0 - (c + d)
在map
中出现过就用count
把map
中key
对应的value
,也就是出现次数统计出来。
最后返回统计值 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>(); 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; } }
复杂度分析 :
时间复杂度: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) { 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 ; } for (int i : record){ if (i < 0 ){ return false ; } } return true ; } }
复杂度分析 :
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); for (int i = 0 ; i < nums.length; i++) { if (nums[i] > 0 ) { return result; } if (i > 0 && nums[i] == nums[i - 1 ]) { 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
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++) { if (nums[i] > 0 && nums[i] > target) { return result; } if (i > 0 && nums[i - 1 ] == nums[i]) { continue ; } for (int j = i + 1 ; j < nums.length; j++) { if (j > i + 1 && nums[j - 1 ] == 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])); while (right > left && nums[right] == nums[right - 1 ]) right--; while (right > left && nums[left] == nums[left + 1 ]) left++; left++; right--; } } } } return result; } }
复杂度分析 :
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--; } } }
复杂度分析 :
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; 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); } }
复杂度分析 :
54. 替换数字 题目 给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number。 例如,对于输入字符串 “a1b2c3”,函数应该将其转换为 “anumberbnumbercnumber”。
示例 示例 示例 1 :
输入 :a1b2c3输出 :anumberbnumbercnumber
示例 2 :
输入 :s = “abcd”, k = 2输出 :”bacd”
提示c
提交记录 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); } }
复杂度分析 :