赞
踩
目录
冒泡排序
插入排序
归并排序
快速排序
如何优化快速排序?
选择排序
代码示例
二分查找
二分查找 II
3. 查找第一个值等于给定值的元素
4. 查找最后一个值等于给定值的元素
5. 查找第一个大于等于给定值的元素
6. 查找最后一个小于等于给定值的元素
7. 两数之和
8. 两数相加
9. 无重复字符的最长子串
10. 寻找两个有序数组的中位数
11. 最长回文子串
12. Z 字形变换
13. 整数反转
14. 字符串转换整数 (atoi)
15. 回文数
16. 正则表达式匹配
17. 盛最多水的容器
18. 整数转罗马数字
19. 罗马数字转整数
20. 最长公共前缀
21. 三数之和
22. 三数之和
23. 电话号码的字母组合
24. 四数之和
25. 删除链表的倒数第 N 个节点
26. 有效的括号
27. 合并两个有序链表
28. 括号生成
29. 合并 K 个排序链表
30. 两两交换链表中的节点
31. K 个一组翻转链表
32. 删除排序数组中的重复项
33. 移除元素
34. 实现 strStr()
35. 两数相除
36. 串联所有单词的子串
37. 下一个排列
38. 最长有效括号
39. 搜索旋转排序数组
40. 在排序数组中查找元素的第一个和最后一个位置
41. 搜索插入位置
42. 有效的数独
43. 解数独
44. 外观数列
45. 组合总和
46. 组合总和 II
47. 缺失的第一个正数
48. 接雨水
49. 字符串相乘
50. 通配符匹配
51. 跳跃游戏 II
52. 全排列
53. 全排列 II
54. 旋转图像
55. 字母异位词分组
56. Pow(x, n)
57. N 皇后
58. N 皇后 II
59. 最大子序和
60. 螺旋矩阵
61. 跳跃游戏
62. 合并区间
63. 插入区间
64. 最后一个单词的长度
65. 螺旋矩阵 II
66. 第 k 个排列
67. 旋转链表
68. 不同路径
69. 不同路径 II
70. 最小路径和
71. 有效数字
72. 加一
73. 二进制求和
74. 文本左右对齐
75. x 的平方根
76. 爬楼梯
77. 简化路径
78. 编辑距离
79. 矩阵置零
80. 搜索二维矩阵
81. 颜色分类
82. 最小覆盖子串
83. 组合
84. 子集
85. 删除排序数组中的重复项 II
86. 搜索旋转排序数组 II
87. 删除排序链表中的重复元素 II
88. 删除排序链表中的重复元素
89. 柱状图中最大的矩形
90. 最大矩形
91. 分隔链表
92. 扰乱字符串
93. 合并两个有序数组
94. 格雷编码
95. 子集 II
96. 解码方法
97. 反转链表 II
98. 复原 IP 地址
99. 二叉树的中序遍历
100. 不同的二叉搜索树 II
101. 不同的二叉搜索树
102. 不同的二叉搜索树
103. 验证二叉搜索树
104. 验证二叉搜索树
前言:二进制、位运算符基础
1.十进制转二进制
原理:给定的数循环除以2,直到商为0或者1为止。将每一步除的结果的余数记录下来,然后反过来就得到相应的二进制了。
比如8转二进制,第一次除以2等于4(余数0),第二次除以2等于2(余数0),第三次除以2等于1(余数0),最后余数1,得到的余数依次是0 0 0 1 ,
反过来就是1000,计算机内部表示数的字节长度是固定的,比如8位,16位,32位。所以在高位补齐,java中字节码是8位的,所以高位补齐就是00001000.
写法位(8)10=(00001000)2;
代码实现:
package sourceCode.hashMap;
public class mapHashCodeTest {
public static void main(String[] args) {
String str = toBinary(8);
System.out.println(str);
}
static String toBinary(int num) {
String str = "";
while (num != 0) {
str = num % 2 + str;
num = num / 2;
}
return str;
}
}
运行结果:1000
2.二进制转十进制
计算也很简单,比如8的二进制表示位00001000,去掉补齐的高位就是1000.此时从个位开始计算2的幂(个位是0,依次往后推)乘以对应位数上的数,然后得到的值想加。
于是有了,(2的0次幂)*0+(2的1次幂)*0+(2的2次幂)*0+(2的3次幂)*1 = 8
代码实现,直接调用Integer.parseInt("",2);
System.out.println(Integer.parseInt("1000",2));
运行结果:8
3.位异或运算(^)
运算规则是:两个数转为二进制,然后从高位开始比较,如果相同则为0,不相同则为1。
比如:8^11.
8转为二进制是1000,11转为二进制是1011.从高位开始比较得到的是:0011.然后二进制转为十进制,就是Integer.parseInt(“0011”,2)=3;
4.位与运算符(&)
运算规则:两个数都转为二进制,然后从高位开始比较,如果两个数都为1则为1,否则为0。
比如:129&128.
129转换成二进制就是10000001,128转换成二进制就是10000000。从高位开始比较得到,得到10000000,即128.
5.位或运算符(|)
运算规则:两个数都转为二进制,然后从高位开始比较,两个数只要有一个为1则为1,否则就为0。
比如:129|128.
129转换成二进制就是10000001,128转换成二进制就是10000000。从高位开始比较得到,得到10000001,即129.
6.位非运算符(~)
运算规则:如果位为0,结果是1,如果位为1,结果是0.
比如:~37
在Java中,所有数据的表示方法都是以补码的形式表示,如果没有特殊说明,Java中的数据类型默认是int,int数据类型的长度是8位,一位是四个字节,就是32字节,32bit.
8转为二进制是100101.
补码后为: 00000000 00000000 00000000 00100101
取反为: 11111111 11111111 11111111 11011010
因为高位是1,所以原码为负数,负数的补码是其绝对值的原码取反,末尾再加1。
因此,我们可将这个二进制数的补码进行还原: 首先,末尾减1得反码:11111111 11111111 11111111 11011001 其次,将各位取反得原码:
00000000 00000000 00000000 00100110,此时二进制转原码为38
所以~37 = -38.
定义一个布尔变量 hasChange
,用来标记每轮是否进行了交换。在每轮遍历开始时,将 hasChange
设置为 false。
若当轮没有发生交换,说明此时数组已经按照升序排列,hashChange
依然是为 false。此时外层循环直接退出,排序结束。
import java.util.Arrays; public class BubbleSort { private static void bubbleSort(int[] nums) { boolean hasChange = true; for (int i = 0, n = nums.length; i < n - 1 && hasChange; ++i) { hasChange = false; for (int j = 0; j < n - i - 1; ++j) { if (nums[j] > nums[j + 1]) { swap(nums, j, j + 1); hasChange = true; } } } } private static void swap(int[] nums, int i, int j) { int t = nums[i]; nums[i] = nums[j]; nums[j] = t; } public static void main(String[] args) { int[] nums = {1, 2, 7, 9, 5, 8}; bubbleSort(nums); System.out.println(Arrays.toString(nums)); } }
空间复杂度 O(1)、时间复杂度 O(n²)。
分情况讨论:
n-1
次比较,两两交换次数为 0,时间复杂度为 O(n),这是最好的情况。n*(n-1)/2
次比较,时间复杂度为 O(n²),这是最坏的情况。因此,时间复杂度是 O(n²),这是一种稳定的排序算法。
稳定是指,两个相等的数,在排序过后,相对位置保持不变。
先来看一个问题。一个有序的数组,我们往里面添加一个新的数据后,如何继续保持数据有序呢?很简单,我们只要遍历数组,找到数据应该插入的位置将其插入即可。
这是一个动态排序的过程,即动态地往有序集合中添加数据,我们可以通过这种方法保持集合中的数据一直有序。而对于一组静态数据,我们也可以借鉴上面讲的插入方法,来进行排序,于是就有了插入排序算法。
那么插入排序具体是如何借助上面的思想来实现排序的呢?
首先,我们将数组中的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素。插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。
与冒泡排序对比:
import java.util.Arrays; public class InsertionSort { private static void insertionSort(int[] nums) { for (int i = 1, j, n = nums.length; i < n; ++i) { int num = nums[i]; for (j = i - 1; j >=0 && nums[j] > num; --j) { nums[j + 1] = nums[j]; } nums[j + 1] = num; } } public static void main(String[] args) { int[] nums = {1, 2, 7, 9, 5, 8}; insertionSort(nums); System.out.println(Arrays.toString(nums)); } }
空间复杂度 O(1),时间复杂度 O(n²)。
分情况讨论:
n*(n-1)/2
次比较,时间复杂度为 O(n²),这是最坏的情况。因此,时间复杂度是 O(n²),这也是一种稳定的排序算法。
归并排序的核心思想是分治,把一个复杂问题拆分成若干个子问题来求解。
归并排序的算法思想是:把数组从中间划分为两个子数组,一直递归地把子数组划分成更小的数组,直到子数组里面只有一个元素的时候开始排序。排序的方法就是按照大小顺序合并两个元素。接着依次按照递归的顺序返回,不断合并排好序的数组,直到把整个数组排好序。
import java.util.Arrays; public class MergeSort { private static void merge(int[] nums, int low, int mid, int high, int[] temp) { int i = low, j = mid + 1, k = low; while (k <= high) { if (i > mid) { temp[k++] = nums[j++]; } else if (j > high) { temp[k++] = nums[i++]; } else if (nums[i] <= nums[j]) { temp[k++] = nums[i++]; } else { temp[k++] = nums[j++]; } } System.arraycopy(tmp, low, nums, low, high - low + 1); } private static void mergeSort(int[] nums, int low, int high, int[] temp) { if (low >= high) { return; } int mid = low + ((high - low) >> 1); mergeSort(nums, low, mid, temp); mergeSort(nums, mid + 1, high, temp); merge(nums, low, mid, high, temp); } private static void mergeSort(int[] nums) { int n = nums.length; int[] temp = new int[n]; mergeSort(nums, 0, n - 1, temp); } public static void main(String[] args) { int[] nums = {1, 2, 7, 4, 5, 3}; mergeSort(nums); System.out.println(Arrays.toString(nums)); } }
空间复杂度 O(n),时间复杂度 O(nlogn)。
对于规模为 n 的问题,一共要进行 log(n) 次的切分,每一层的合并复杂度都是 O(n),所以整体时间复杂度为 O(nlogn)。
由于合并 n 个元素需要分配一个大小为 n 的额外数组,所以空间复杂度为 O(n)。
这是一种稳定的排序算法。
快速排序也采用了分治的思想:把原始的数组筛选成较小和较大的两个子数组,然后递归地排序两个子数组。
import java.util.Arrays; public class QuickSort { private static void quickSort(int[] nums) { quickSort(nums, 0, nums.length - 1); } private static void quickSort(int[] nums, int low, int high) { if (low >= high) { return; } int[] p = partition(nums, low, high); quickSort(nums, low, p[0] - 1); quickSort(nums, p[0] + 1, high); } private static int[] partition(int[] nums, int low, int high) { int less = low - 1, more = high; while (low < more) { if (nums[low] < nums[high]) { swap(nums, ++less, low++); } else if (nums[low] > nums[high]) { swap(nums, --more, low); } else { ++low; } } swap(nums, more, high); return new int[] {less + 1, more}; } private static void swap(int[] nums, int i, int j) { int t = nums[i]; nums[i] = nums[j]; nums[j] = t; } public static void main(String[] args) { int[] nums = {1, 2, 7, 4, 5, 3}; quickSort(nums); System.out.println(Arrays.toString(nums)); } }
空间复杂度 O(logn),时间复杂度 O(nlogn)。
对于规模为 n 的问题,一共要进行 log(n) 次的切分,和基准值进行 n-1 次比较,n-1 次比较的时间复杂度是 O(n),所以快速排序的时间复杂度为 O(nlogn)。
但是,如果每次在选择基准值的时候,都不幸地选择了子数组里的最大或最小值。即每次把把数组分成了两个更小长度的数组,其中一个长度为 1,另一个的长度是子数组的长度减 1。这样的算法复杂度变成 O(n²)。
和归并排序不同,快速排序在每次递归的过程中,只需要开辟 O(1) 的存储空间来完成操作来实现对数组的修改;而递归次数为 logn,所以它的整体空间复杂度完全取决于压堆栈的次数。
前面讲到,最坏情况下快速排序的时间复杂度是 O(n²),实际上,这种 O(n²) 时间复杂度出现的主要原因还是因为我们基准值选得不够合理。最理想的基准点是:被基准点分开的两个子数组中,数据的数量差不多。
如果很粗暴地直接选择第一个或者最后一个数据作为基准值,不考虑数据的特点,肯定会出现之前讲的那样,在某些情况下,排序的最坏情况时间复杂度是 O(n²)。
有两个比较常用的分区算法。
我们从区间的首、尾、中间,分别取出一个数,然后对比大小,取这 3 个数的中间值作为分区点。这样每间隔某个固定的长度,取数据出来比较,将中间值作为分区点的分区算法,肯定要比单纯取某一个数据更好。但是,如果要排序的数组比较大,那“三数取中”可能就不够了,可能要“五数取中”或者“十数取中”。
随机法就是每次从要排序的区间中,随机选择一个元素作为分区点。这种方法并不能保证每次分区点都选的比较好,但是从概率的角度来看,也不大可能会出现每次分区点都选的很差的情况,所以平均情况下,这样选的分区点是比较好的。时间复杂度退化为最糟糕的 O(n²) 的情况,出现的可能性不大。
选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。
import java.util.Arrays; public class SelectionSort { private static void selectionSort(int[] nums) { for (int i = 0, n = nums.length; i < n - 1; ++i) { int minIndex = i; for (int j = i; j < n; ++j) { if (nums[j] < nums[minIndex]) { minIndex = j; } } swap(nums, minIndex, i); } } private static void swap(int[] nums, int i, int j) { int t = nums[i]; nums[i] = nums[j]; nums[j] = t; } public static void main(String[] args) { int[] nums = {1, 2, 7, 9, 5, 8}; selectionSort(nums); System.out.println(Arrays.toString(nums)); } }
空间复杂度 O(1),时间复杂度 O(n²)。
那选择排序是稳定的排序算法吗?
答案是否定的,选择排序是一种不稳定的排序算法。选择排序每次都要找剩余未排序元素中的最小值,并和前面的元素交换位置,这样破坏了稳定性。
比如 5,8,5,2,9 这样一组数据,使用选择排序算法来排序的话,第一次找到最小元素 2,与第一个 5 交换位置,那第一个 5 和中间的 5 顺序就变了,所以就不稳定了。正是因此,相对于冒泡排序和插入排序,选择排序就稍微逊色了。
二分查找是一种非常高效的查找算法,高效到什么程度呢?我们来分析一下它的时间复杂度。
假设数据大小是 n,每次查找后数据都会缩小为原来的一半,也就是会除以 2。最坏情况下,直到查找区间被缩小为空,才停止。
被查找区间的大小变化为:
n, n/2, n/4, n/8, ..., n/(2^k)
可以看出来,这是一个等比数列。其中 n/(2^k)=1
时,k 的值就是总共缩小的次数。而每一次缩小操作只涉及两个数据的大小比较,所以,经过了 k 次区间缩小操作,时间复杂度就是 O(k)。通过 n/(2^k)=1
,我们可以求得 k=log2n
,所以时间复杂度就是 O(logn)。
注意容易出错的 3 个地方。
low <= high
,而不是 low < high
;mid = (low + high) / 2
,但是如果 low 和 high 比较大的话,low + high
可能会溢出,所以这里写为 mid = low + ((high - low) >> 1)
;low = mid + 1
、high = mid - 1
。非递归实现:
public class BinarySearch { private static int search(int[] nums, int low, int high, int val) { while (low <= high) { int mid = low + ((high -low) >> 1); if (nums[mid] == val) { return mid; } else if (nums[mid] < val) { low = mid + 1; } else { high = mid - 1; } } return -1; } /** * 二分查找(非递归) * * @param nums 有序数组 * @param val 要查找的值 * @return 要查找的值在数组中的索引位置 */ private static int search(int[] nums, int val) { return search(nums, 0, nums.length - 1, val); } public static void main(String[] args) { int[] nums = {1, 2, 5, 7, 8, 9}; // 非递归查找 int r1 = search(nums, 7); System.out.println(r1); } }
递归实现:
public class BinarySearch { private static int searchRecursive(int[] nums, int low, int high, int val) { while (low <= high) { int mid = low + ((high - low) >> 1); if (nums[mid] == val) { return mid; } else if (nums[mid] < val) { return searchRecursive(nums, mid + 1, high, val); } else { return searchRecursive(nums, low, mid - 1, val); } } return -1; } /** * 二分查找(递归) * * @param nums 有序数组 * @param val 要查找的值 * @return 要查找的值在数组中的索引位置 */ private static int searchRecursive(int[] nums, int val) { return searchRecursive(nums, 0, nums.length - 1, val); } public static void main(String[] args) { int[] nums = {1, 2, 5, 7, 8, 9}; // 递归查找 int r2 = searchRecursive(nums, 7); System.out.println(r2); } }
前面讲的二分查找算法,是最为简单的一种,在不存在重复元素的有序数组中,查找值等于给定值的元素。
接下来,我们来看看二分查找算法四种常见的变形问题,分别是:
public static int search(int[] nums, int val) { int n = nums.length; int low = 0, high = n - 1; while (low <= high) { int mid = low + ((high - low) >> 1); if (nums[mid] < val) { low = mid + 1; } else if (nums[mid] > val) { high = mid - 1; } else { // 如果nums[mid]是第一个元素,或者nums[mid-1]不等于val // 说明nums[mid]就是第一个值为给定值的元素 if (mid == 0 || nums[mid - 1] != val) { return mid; } high = mid - 1; } } return -1; }
public static int search(int[] nums, int val) { int n = nums.length; int low = 0, high = n - 1; while (low <= high) { int mid = low + ((high - low) >> 1); if (nums[mid] < val) { low = mid + 1; } else if (nums[mid] > val) { high = mid - 1; } else { // 如果nums[mid]是最后一个元素,或者nums[mid+1]不等于val // 说明nums[mid]就是最后一个值为给定值的元素 if (mid == n - 1 || nums[mid + 1] != val) { return mid; } low = mid + 1; } } return -1; }
public static int search(int[] nums, int val) { int low = 0, high = nums.length - 1; while (low <= high) { int mid = low + ((high - low) >> 1); if (nums[mid] < val) { low = mid + 1; } else { // 如果nums[mid]是第一个元素,或者nums[mid-1]小于val // 说明nums[mid]就是第一个大于等于给定值的元素 if (mid == 0 || nums[mid - 1] < val) { return mid; } high = mid - 1; } } return -1; }
public static int search(int[] nums, int val) { int n = nums.length; int low = 0, high = n - 1; while (low <= high) { int mid = low + ((high - low) >> 1); if (nums[mid] > val) { high = mid - 1; } else { // 如果nums[mid]是最后一个元素,或者nums[mid+1]大于val // 说明nums[mid]就是最后一个小于等于给定值的元素 if (mid == n - 1 || nums[mid + 1] > val) { return mid; } low = mid + 1; } } return -1; }
给定一个整数数组 nums
和一个目标值 target
,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9 因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]
用哈希表(字典)存放数组值以及对应的下标。
遍历数组,当发现 target - nums[i]
在哈希表中,说明找到了目标值。
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
helper = {}
for i, v in enumerate(nums):
num = target - v
if num in helper:
return [helper[num], i]
helper[v] = i
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0, n = nums.length; i < n; ++i) {
int num = target - nums[i];
if (map.containsKey(num)) {
return new int[]{map.get(num), i};
}
map.put(nums[i], i);
}
return null;
}
}
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 输出:7 -> 0 -> 8 原因:342 + 465 = 807
# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode: carry = 0 dummy = ListNode(-1) cur = dummy while l1 or l2 or carry: t = (0 if not l1 else l1.val) + (0 if not l2 else l2.val) + carry carry = t // 10 cur.next = ListNode(t % 10) cur = cur.next l1 = None if not l1 else l1.next l2 = None if not l2 else l2.next return dummy.next
/** * 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 addTwoNumbers(ListNode l1, ListNode l2) { int carry = 0; ListNode dummy = new ListNode(-1); ListNode cur = dummy; while (l1 != null || l2 != null || carry != 0) { int t = (l1 == null ? 0 : l1.val) + (l2 == null ? 0 : l2.val) + carry; carry = t / 10; cur.next = new ListNode(t % 10); cur = cur.next; l1 = l1 == null ? null : l1.next; l2 = l2 == null ? null : l2.next; } return dummy.next; } }
/** * Definition for singly-linked list. * public class ListNode { * public int val; * public ListNode next; * public ListNode(int val=0, ListNode next=null) { * this.val = val; * this.next = next; * } * } */ public class Solution { public ListNode AddTwoNumbers(ListNode l1, ListNode l2) { ListNode dummy = new ListNode(-1); ListNode cur = dummy; var carry = 0; while (l1 != null || l2 != null || carry != 0) { int t = (l1 == null ? 0 : l1.val) + (l2 == null ? 0 : l2.val) + carry; carry = t / 10; cur.next = new ListNode(t % 10); cur = cur.next; l1 = l1 == null ? null : l1.next; l2 = l2 == null ? null : l2.next; } return dummy.next; } }
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其
长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b"
,所以其长度为 1。
示例 3:
输入: "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是"wke"
,所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke"
是一个子序列,不是子串。
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> map = new HashMap<>();
int max = 0;
for (int fast = 0, slow = 0; fast < s.length(); fast ++) {
if (map.containsKey(s.charAt(fast))) {
int target = map.get(s.charAt(fast)) + 1;
slow = target < slow ? slow : target;
}
map.put(s.charAt(fast), fast);
max = Math.max(max, fast - slow + 1);
}
return max;
}
}
给定两个大小为 m 和 n 的有序数组 nums1
和 nums2
。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1
和 nums2
不会同时为空。
示例 1:
nums1 = [1, 3] nums2 = [2] 则中位数是 2.0
示例 2:
nums1 = [1, 2] nums2 = [3, 4] 则中位数是 (2 + 3)/2 = 2.5
class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { int len1 = nums1.length; int len2 = nums2.length; if (len1 > len2) { int[] tmp = nums1; nums1 = nums2; nums2 = tmp; int t = len1; len1 = len2; len2 = t; } int min = 0; int max = len1; int m = (len1 + len2 + 1) / 2; while (min <= max) { int i = (min + max) / 2; int j = m - i; if (i > min && nums1[i - 1] > nums2[j]) { --max; } else if (i < max && nums2[j - 1] > nums1[i]) { ++min; } else { int maxLeft = i == 0 ? nums2[j - 1] : j == 0 ? nums1[i - 1] : Math.max(nums1[i - 1], nums2[j - 1]); if (((len1 + len2) & 1) == 1) { return maxLeft; } int minRight = i == len1 ? nums2[j] : j == len2 ? nums1[i] : Math.min(nums2[j], nums1[i]); return (maxLeft + minRight) / 2.0; } } return 0; } }
给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
示例 1:
输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd" 输出: "bb"
class Solution { public String longestPalindrome(String s) { if (s == null || s.length() < 1) { return ""; } String str = ""; char[] chars = s.toCharArray(); int len = chars.length; boolean[][] res = new boolean[len][len]; int start = 0; int max = 1; for (int i = 0; i < len; ++i) { for (int j = 0; j <= i; ++j) { res[j][i] = i - j < 2 ? chars[j] == chars[i] : res[j + 1][i - 1] && chars[j] == chars[i]; if (res[j][i] && max < i - j + 1) { max = i - j + 1; start = j; } } } return s.substring(start, start + max); } }
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "LEETCODEISHIRING"
行数为 3 时,排列如下:
L C I R E T O E S I I G E D H N
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"
。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
示例 1:
输入: s = "LEETCODEISHIRING", numRows = 3 输出: "LCIRETOESIIGEDHN"
示例 2:
输入: s = "LEETCODEISHIRING", numRows = 4 输出: "LDREOEIIECIHNTSG" 解释: L D R E O E I I E C I H N T S G
class Solution { public String convert(String s, int numRows) { if (numRows == 1) return s; StringBuilder result = new StringBuilder(); int group = 2 * numRows - 2; for (int i = 1; i <= numRows; i++) { int interval = 2 * numRows - 2 * i; if (i == numRows) interval = 2 * numRows - 2; int index = i; while (index <= s.length()) { result.append(s.charAt(index - 1)); index += interval; interval = group - interval; if (interval == 0) interval = group; } } return result.toString(); } }
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例 1:
输入: 123 输出: 321
示例 2:
输入: -123 输出: -321
示例 3:
输入: 120 输出: 21
注意:
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
/* class Solution { public int reverse(int x) { if (x == 0) { return x; } long tmp = x; boolean isPositive = true; if (tmp < 0) { isPositive = false; tmp = -tmp; } long val = Long.parseLong(new StringBuilder(String.valueOf(tmp)).reverse().toString()); return isPositive ? (val > Integer.MAX_VALUE ? 0 : (int) val) : (-val < Integer.MIN_VALUE ? 0 : (int) (-val)); } } */ class Solution { public int reverse(int x) { long res = 0; // 考虑负数情况,所以这里条件为: x != 0 while (x != 0) { res = res * 10 + (x % 10); x /= 10; } return (res < Integer.MIN_VALUE || res > Integer.MAX_VALUE) ? 0 : (int) res; } }
请你来实现一个 atoi
函数,使其能将字符串转换成整数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
示例 1:
输入: "42" 输出: 42
示例 2:
输入: " -42" 输出: -42 解释: 第一个非空白字符为 '-', 它是一个负号。 我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:
输入: "4193 with words" 输出: 4193 解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。
示例 4:
输入: "words and 987" 输出: 0 解释: 第一个非空字符是 'w', 但它不是数字或正、负号。 因此无法执行有效的转换。
示例 5:
输入: "-91283472332" 输出: -2147483648 解释: 数字 "-91283472332" 超过 32 位有符号整数范围。 因此返回 INT_MIN (−231) 。
遍历字符串,注意做溢出处理。
class Solution: def myAtoi(self, s: str) -> int: if not s: return 0 n = len(s) if n == 0: return 0 i = 0 while s[i] == ' ': i += 1 # 仅包含空格 if i == n: return 0 sign = -1 if s[i] == '-' else 1 if s[i] in ['-', '+']: i += 1 res, flag = 0, (2 ** 31 - 1) // 10 while i < n: # 非数字,跳出循环体 if not s[i].isdigit(): break c = int(s[i]) # 溢出判断 if res > flag or (res == flag and c > 7): return 2 ** 31 - 1 if sign > 0 else -2 ** 31 res = res * 10 + c i += 1 return sign * res
class Solution { public int myAtoi(String s) { if (s == null) return 0; int n = s.length(); if (n == 0) return 0; int i = 0; while (s.charAt(i) == ' ') { // 仅包含空格 if (++i == n) return 0; } int sign = 1; if (s.charAt(i) == '-') sign = -1; if (s.charAt(i) == '-' || s.charAt(i) == '+') ++i; int res = 0, flag = Integer.MAX_VALUE / 10; for (; i < n; ++i) { // 非数字,跳出循环体 if (s.charAt(i) < '0' || s.charAt(i) > '9') break; // 溢出判断 if (res > flag || (res == flag && s.charAt(i) > '7')) return sign > 0 ? Integer.MAX_VALUE : Integer.MIN_VALUE; res = res * 10 + (s.charAt(i) - '0'); } return sign * res; } }
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例 1:
输入: 121 输出: true
示例 2:
输入: -121 输出: false 解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入: 10 输出: false 解释: 从右向左读, 为 01 。因此它不是一个回文数。
进阶:
你能不将整数转为字符串来解决这个问题吗?
class Solution:
def isPalindrome(self, x: int) -> bool:
if x < 0:
return False
y, t = 0, x
while t:
y = y * 10 + t % 10
t //= 10
return x == y
class Solution {
public boolean isPalindrome(int x) {
if (x < 0) return false;
int y = 0, t = x;
while (t != 0) {
y = y * 10 + t % 10;
t /= 10;
}
return x == y;
}
}
给你一个字符串 s
和一个字符规律 p
,请你来实现一个支持 '.'
和 '*'
的正则表达式匹配。
'.' 匹配任意单个字符 '*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s
的,而不是部分字符串。
说明:
s
可能为空,且只包含从 a-z
的小写字母。p
可能为空,且只包含从 a-z
的小写字母,以及字符 .
和 *
。示例 1:
输入: s = "aa" p = "a" 输出: false 解释: "a" 无法匹配 "aa" 整个字符串。
示例 2:
输入: s = "aa" p = "a*" 输出: true 解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例 3:
输入: s = "ab" p = ".*" 输出: true 解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。
示例 4:
输入: s = "aab" p = "c*a*b" 输出: true 解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例 5:
输入: s = "mississippi" p = "mis*is*p*." 输出: false
class Solution { public boolean isMatch(String s, String p) { boolean[] match = new boolean[s.length() + 1]; match[s.length()] = true; for (int i = p.length() - 1; i >= 0; i--) { if (p.charAt(i) == '*') { for (int j = s.length() - 1; j >= 0; j--) { match[j] = match[j] || (match[j + 1] && (p.charAt(i - 1) == '.' || (p.charAt(i - 1) == s.charAt(j)))); } i--; } else { for (int j = 0; j < s.length(); j++) { match[j] = match[j + 1] && (p.charAt(i) == '.' || (p.charAt(i) == s.charAt(j))); } match[s.length()] = false; } } return match[0]; } }
给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例:
输入:[1,8,6,2,5,4,8,3,7] 输出:49
class Solution { public int maxArea(int[] height) { int start = 0, end = height.length - 1, maxArea = 0; while (start < end) { int hs = height[start]; int he = height[end]; int l = end - start; if (hs > he) { maxArea = Math.max(he * l, maxArea); end--; } else { maxArea = Math.max(hs * l, maxArea); start++; } } return maxArea; } }
罗马数字包含以下七种字符: I
, V
, X
, L
,C
,D
和 M
。
字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000
例如, 罗马数字 2 写做 II
,即为两个并列的 1。12 写做 XII
,即为 X
+ II
。 27 写做 XXVII
, 即为 XX
+ V
+ II
。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII
,而是 IV
。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX
。这个特殊的规则只适用于以下六种情况:
I
可以放在 V
(5) 和 X
(10) 的左边,来表示 4 和 9。X
可以放在 L
(50) 和 C
(100) 的左边,来表示 40 和 90。 C
可以放在 D
(500) 和 M
(1000) 的左边,来表示 400 和 900。给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。
示例 1:
输入: 3 输出: "III"
示例 2:
输入: 4 输出: "IV"
示例 3:
输入: 9 输出: "IX"
示例 4:
输入: 58 输出: "LVIII" 解释: L = 50, V = 5, III = 3.
示例 5:
输入: 1994 输出: "MCMXCIV" 解释: M = 1000, CM = 900, XC = 90, IV = 4.
class Solution {
private final String[] M = {"", "M", "MM", "MMM"};
private final String[] C = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
private final String[] X = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
private final String[] I = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
public String intToRoman(int num) {
return M[num / 1000] + C[(num % 1000) / 100] + X[(num % 100) / 10] + I[num % 10];
}
}
罗马数字包含以下七种字符: I
, V
, X
, L
,C
,D
和 M
。
字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000
例如, 罗马数字 2 写做 II
,即为两个并列的 1。12 写做 XII
,即为 X
+ II
。 27 写做 XXVII
, 即为 XX
+ V
+ II
。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII
,而是 IV
。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX
。这个特殊的规则只适用于以下六种情况:
I
可以放在 V
(5) 和 X
(10) 的左边,来表示 4 和 9。X
可以放在 L
(50) 和 C
(100) 的左边,来表示 40 和 90。 C
可以放在 D
(500) 和 M
(1000) 的左边,来表示 400 和 900。给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。
示例 1:
输入: "III" 输出: 3
示例 2:
输入: "IV" 输出: 4
示例 3:
输入: "IX" 输出: 9
示例 4:
输入: "LVIII" 输出: 58 解释: L = 50, V= 5, III = 3.
示例 5:
输入: "MCMXCIV" 输出: 1994 解释: M = 1000, CM = 900, XC = 90, IV = 4.
class Solution { public int romanToInt(String s) { if (s == null || s.isEmpty()) { return 0; } String str = "IVXLCDM"; int[] num = {1, 5, 10, 50, 100, 500, 1000}; int ans = 0; int i = 0; int pre = 999, cur = 0; while (i < s.length()) { cur = str.indexOf(s.charAt(i++)); if (pre < cur) { ans = ans + num[cur] - 2 * num[pre]; } else { ans += num[cur]; } pre = cur; } return ans; } }
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""
。
示例 1:
输入: ["flower","flow","flight"] 输出: "fl"
示例 2:
输入: ["dog","racecar","car"] 输出: "" 解释: 输入不存在公共前缀。
说明:
所有输入只包含小写字母 a-z
。
class Solution { public String longestCommonPrefix(String[] strs) { if (strs == null || strs.length == 0) { return ""; } if (strs.length == 1) { return strs[0]; } char[] chars = strs[0].toCharArray(); int i = 0; boolean flag = true; for (; i < chars.length; ++i) { char ch = chars[i]; for (int j = 1; j < strs.length; ++j) { if (strs[j].length() <= i) { flag = false; break; } if (strs[j].charAt(i) != ch) { flag = false; break; } } if (!flag) { break; } } return strs[0].substring(0, i); } }
给你一个包含 n 个整数的数组 nums
,判断 nums
中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4], 满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
“排序 + 双指针”实现。
class Solution: def threeSum(self, nums: List[int]) -> List[List[int]]: if nums is None or len(nums) < 3: return [] nums.sort() n = len(nums) res = [] for i in range(n - 2): if i > 0 and nums[i] == nums[i - 1]: continue p, q = i + 1, n - 1 while p < q: if p > i + 1 and nums[p] == nums[p - 1]: p += 1 continue if q < n - 1 and nums[q] == nums[q + 1]: q -= 1 continue if nums[i] + nums[p] + nums[q] < 0: p += 1 elif nums[i] + nums[p] + nums[q] > 0: q -= 1 else: res.append([nums[i], nums[p], nums[q]]) p += 1 q -= 1 return res
class Solution { public List<List<Integer>> threeSum(int[] nums) { int n; if (nums == null || (n = nums.length) < 3) { return Collections.emptyList(); } Arrays.sort(nums); List<List<Integer>> res = new ArrayList<>(); for (int i = 0; i < n - 2; ++i) { if (i > 0 && nums[i] == nums[i - 1]) { continue; } int p = i + 1, q = n - 1; while (p < q) { if (p > i + 1 && nums[p] == nums[p - 1]) { ++p; continue; } if (q < n - 1 && nums[q] == nums[q + 1]) { --q; continue; } if (nums[p] + nums[q] + nums[i] < 0) { ++p; } else if (nums[p] + nums[q] + nums[i] > 0) { --q; } else { res.add(Arrays.asList(nums[p], nums[q], nums[i])); ++p; --q; } } } return res; } }
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例:
输入:"23" 输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。
class Solution { public List<String> letterCombinations(String digits) { char[] cs = digits.toCharArray(); List<String> result = new ArrayList<>(); for (char a : cs) { char[] charArray; switch (a) { case '2': charArray = new char[]{'a','b','c'}; break; case '3': charArray = new char[]{'d','e','f'}; break; case '4': charArray = new char[]{'g','h','i'}; break; case '5': charArray = new char[]{'j','k','l'}; break; case '6': charArray = new char[]{'m','n','o'}; break; case '7': charArray = new char[]{'p','q','r','s'}; break; case '8': charArray = new char[]{'t','u','v'}; break; case '9': charArray = new char[]{'w','x','y','z'}; break; default: return null; } if (result.size() == 0) { for (char aCharArray : charArray) result.add(String.valueOf(aCharArray)); } else { List<String> cache = new ArrayList<>(); for (String string : result) { for (char aCharArray : charArray) cache.add(string + aCharArray); } result = cache; } } return result; } }
给定一个包含 n 个整数的数组 nums
和一个目标值 target
,判断 nums
中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target
相等?找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。 满足要求的四元组集合为: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]
“排序 + 双指针”实现。
class Solution: def fourSum(self, nums: List[int], target: int) -> List[List[int]]: res = [] if nums is None or len(nums) < 4: return res n = len(nums) nums.sort() for i in range(n - 3): if i > 0 and nums[i] == nums[i - 1]: continue for j in range(i + 1, n - 2): if j > i + 1 and nums[j] == nums[j - 1]: continue p, q = j + 1, n - 1 while p < q: if p > j + 1 and nums[p] == nums[p - 1]: p += 1 continue if q < n - 1 and nums[q] == nums[q + 1]: q -= 1 continue t = nums[i] + nums[j] + nums[p] + nums[q] if t == target: res.append([nums[i], nums[j], nums[p], nums[q]]) p += 1 q -= 1 elif t < target: p += 1 else: q -= 1 return res
class Solution { public List<List<Integer>> fourSum(int[] nums, int target) { int n; if (nums == null || (n = (nums.length)) < 4) { return Collections.emptyList(); } Arrays.sort(nums); List<List<Integer>> res = new ArrayList<>(); for (int i = 0; i < n - 3; ++i) { if (i > 0 && nums[i] == nums[i - 1]) { continue; } for (int j = i + 1; j < n - 2; ++j) { if (j > i + 1 && nums[j] == nums[j - 1]) { continue; } int p = j + 1, q = n - 1; while (p < q) { if (p > j + 1 && nums[p] == nums[p - 1]) { ++p; continue; } if (q < n - 1 && nums[q] == nums[q + 1]) { --q; continue; } int t = nums[i] + nums[j] + nums[p] + nums[q]; if (t == target) { res.add(Arrays.asList(nums[i], nums[j], nums[p], nums[q])); ++p; --q; } else if (t < target) { ++p; } else { --q; } } } } return res; } }
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2. 当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode: dummy = ListNode(0, head) p = q = dummy for i in range(n): p = p.next while p.next is not None: p = p.next q = q.next q.next = q.next.next return dummy.next
/** * 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(0, head); ListNode p = dummy, q = dummy; while (n-- > 0) { p = p.next; } while (p.next != null) { p = p.next; q = q.next; } q.next = q.next.next; return dummy.next; } }
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串,判断字符串是否有效。
有效字符串需满足:
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()" 输出: true
示例 2:
输入: "()[]{}" 输出: true
示例 3:
输入: "(]" 输出: false
示例 4:
输入: "([)]" 输出: false
示例 5:
输入: "{[]}" 输出: true
栈实现。
遍历括号字符串,遇到左括号时,将左括号压入栈中;遇到右括号时,弹出栈顶元素(栈若为空,直接返回 false),判断是否是相同类型的括号。若不匹配,直接返回 false。
遍历结束,栈若为空,说明括号字符串有效。
class Solution:
def isValid(self, s: str) -> bool:
q = []
parentheses = {'()', '[]', '{}'}
for ch in s:
if ch in '([{':
q.append(ch)
elif not q or q.pop() + ch not in parentheses:
return False
return not q
class Solution { public boolean isValid(String s) { char[] chars = s.toCharArray(); Deque<Character> q = new ArrayDeque<>(); for (char ch : chars) { boolean left = ch == '(' || ch == '[' || ch == '{'; if (left) q.push(ch); else if (q.isEmpty() || !match(q.pop(), ch)) return false; } return q.isEmpty(); } private boolean match(char l, char r) { return (l == '(' && r == ')') || (l == '{' && r == '}') || (l == '[' && r == ']'); } }
class Solution { public: bool isValid(string s) { stack<char> q; for (int i = 0, n = s.length(); i < n; ++i) { if (s[i] == '{' || s[i] == '[' || s[i] == '(') q.push(s[i]); else if (q.empty() || !match(q.top(), s[i])) return false; else q.pop(); } return q.empty(); } private: bool match(char l, char r) { return (l == '(' && r == ')') || (l == '[' && r == ']') || (l == '{' && r == '}'); } };
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4
# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode: dummy = ListNode() cur = dummy while l1 and l2: if l1.val <= l2.val: cur.next = l1 l1 = l1.next else: cur.next = l2 l2 = l2.next cur = cur.next cur.next = l1 or l2 return dummy.next
/** * 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 mergeTwoLists(ListNode l1, ListNode l2) { ListNode dummy = new ListNode(0); ListNode cur = dummy; while (l1 != null && l2 != null) { if (l1.val <= l2.val) { cur.next = l1; l1 = l1.next; } else { cur.next = l2; l2 = l2.next; } cur = cur.next; } cur.next = l1 == null ? l2 : l1; return dummy.next; } }
给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
例如,给出 n = 3,生成结果为:
[ "((()))", "(()())", "(())()", "()(())", "()()()" ]
class Solution { public List<String> generateParenthesis(int n) { List<String> res = new ArrayList<>(); dfs(res, "", 0, 0, n); return res; } private void dfs(List<String> res, String ans, int l, int r, int length) { if (ans.length() == length * 2) { res.add(ans); return; } if (l < length) { dfs(res, ans + "(", l + 1, r, length); } if (r < l) { dfs(res, ans + ")", l, r + 1, length); } } }
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入: [ 1->4->5, 1->3->4, 2->6 ] 输出: 1->1->2->3->4->4->5->6
合并前后两个链表,结果放在后一个链表位置上,依次循环下去。
# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: def mergeKLists(self, lists: List[ListNode]) -> ListNode: if not lists: return None n = len(lists) for i in range(1, n): lists[i] = self.mergeTwoLists(lists[i - 1], lists[i]) return lists[n - 1] def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode: dummy = ListNode() cur = dummy while l1 and l2: if l1.val <= l2.val: cur.next = l1 l1 = l1.next else: cur.next = l2 l2 = l2.next cur = cur.next cur.next = l1 or l2 return dummy.next
/** * 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 mergeKLists(ListNode[] lists) { int n; if (lists == null || (n = lists.length) == 0) { return null; } for (int i = 1; i < n; ++i) { lists[i] = mergeTwoLists(lists[i - 1], lists[i]); } return lists[n - 1]; } private ListNode mergeTwoLists(ListNode l1, ListNode l2) { ListNode dummy = new ListNode(0); ListNode cur = dummy; while (l1 != null && l2 != null) { if (l1.val <= l2.val) { cur.next = l1; l1 = l1.next; } else { cur.next = l2; l2 = l2.next; } cur = cur.next; } cur.next = l1 == null ? l2 : l1; return dummy.next; } }
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
给定1->2->3->4
, 你应该返回2->1->4->3
.
# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: def swapPairs(self, head: ListNode) -> ListNode: dummy = ListNode(next=head) pre, cur = dummy, head while cur and cur.next: t = cur.next cur.next = t.next t.next = cur pre.next = t pre = cur cur = pre.next return dummy.next
/** * 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 dummy = new ListNode(0, head); ListNode pre = dummy, cur = head; while (cur != null && cur.next != null) { ListNode t = cur.next; cur.next = t.next; t.next = cur; pre.next = t; pre = cur; cur = pre.next; } return dummy.next; } }
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode* swapPairs(ListNode* head) { ListNode* dummy = new ListNode(0, head); ListNode* pre = dummy; ListNode* cur = head; while (cur != nullptr && cur->next != nullptr) { ListNode* t = cur->next; cur->next = t->next; t->next = cur; pre->next = t; pre = cur; cur = pre->next; } return dummy->next; } };
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
示例:
给你这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5
说明:
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode reverseKGroup(ListNode head, int k) { if(head == null || k < 2) { return head; } int num = 0; ListNode pNode = head; ListNode lastNode = new ListNode(0); ListNode reNode = lastNode; lastNode.next = head; while (pNode != null) { num++; if(num >= k) { num = 0; ListNode tempNode = pNode.next; reverse(lastNode.next, k); // k 个节点的尾节点指向下一组的头节点 lastNode.next.next = tempNode; // 上一组的尾节点指向当前 k 个节点的头节点 tempNode = lastNode.next; lastNode.next = pNode; lastNode = tempNode; pNode = lastNode.next; } else { pNode = pNode.next; } } return reNode.next; } private ListNode reverse(ListNode node, int i) { if(i <= 1 || node.next == null) { return node; } ListNode lastNode = reverse(node.next, i - 1); lastNode.next = node; return node; } }
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2], 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为1
,
2
。 你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4], 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为0
,
1
,
2
,
3
,
4
。 你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 int len = removeDuplicates(nums); // 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。 for (int i = 0; i < len; i++) { print(nums[i]); }
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
cnt, n = 0, len(nums)
for i in range(1, n):
if nums[i] == nums[i - 1]:
cnt += 1
else:
nums[i - cnt] = nums[i]
return n - cnt
class Solution {
public int removeDuplicates(int[] nums) {
int cnt = 0, n = nums.length;
for (int i = 1; i < n; ++i) {
if (nums[i] == nums[i - 1]) ++cnt;
else nums[i - cnt] = nums[i];
}
return n - cnt;
}
}
/**
* @param {number[]} nums
* @return {number}
*/
var removeDuplicates = function (nums) {
let cnt = 0;
const n = nums.length;
for (let i = 1; i < n; ++i) {
if (nums[i] == nums[i - 1]) ++cnt;
else nums[i - cnt] = nums[i];
}
return n - cnt;
};
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,1,2,2,3,0,4,2], val = 2, 函数应该返回新的长度5
, 并且 nums 中的前五个元素为
0
,
1
,
3
,
0
, 4。 注意这五个元素可为任意顺序。 你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝 int len = removeElement(nums, val); // 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。 for (int i = 0; i < len; i++) { print(nums[i]); }
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
cnt, n = 0, len(nums)
for i in range(n):
if nums[i] == val:
cnt += 1
else:
nums[i - cnt] = nums[i]
return n - cnt
class Solution {
public int removeElement(int[] nums, int val) {
int cnt = 0, n = nums.length;
for (int i = 0; i < n; ++i) {
if (nums[i] == val) {
++cnt;
} else {
nums[i - cnt] = nums[i];
}
}
return n - cnt;
}
}
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int cnt = 0, n = nums.size();
for (int i = 0; i < n; ++i) {
if (nums[i] == val) {
++cnt;
} else {
nums[i - cnt] = nums[i];
}
}
return n - cnt;
}
};
实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1:
输入: haystack = "hello", needle = "ll" 输出: 2
示例 2:
输入: haystack = "aaaaa", needle = "bba" 输出: -1
说明:
当 needle
是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle
是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
class Solution { public int strStr(String haystack, String needle) { if ("".equals(needle)) { return 0; } int len1 = haystack.length(); int len2 = needle.length(); int p = 0; int q = 0; while (p < len1) { if (haystack.charAt(p) == needle.charAt(q)) { if (len2 == 1) { return p; } ++p; ++q; } else { p -= q - 1; q = 0; } if (q == len2) { return p - q; } } return -1; } }
给定两个整数,被除数 dividend
和除数 divisor
。将两数相除,要求不使用乘法、除法和 mod 运算符。
返回被除数 dividend
除以除数 divisor
得到的商。
整数除法的结果应当截去(truncate
)其小数部分,例如:truncate(8.345) = 8
以及 truncate(-2.7335) = -2
示例 1:
输入: dividend = 10, divisor = 3 输出: 3 解释: 10/3 = truncate(3.33333..) = truncate(3) = 3
示例 2:
输入: dividend = 7, divisor = -3 输出: -2 解释: 7/-3 = truncate(-2.33333..) = truncate(-2) = 3
提示:
class Solution { public int divide(int dividend, int divisor) { if(dividend == 0 || divisor == 1) { return dividend; } if(divisor == 0 || (dividend == Integer.MIN_VALUE && divisor == -1)) { return Integer.MAX_VALUE; } // 商的符号,true 为正,false 为负 boolean flag = true; if((dividend < 0 && divisor > 0) || (dividend > 0 && divisor < 0)) { flag = false; } long dividendLong = Math.abs((long)dividend); long divisorLong = Math.abs((long)divisor); int re = 0; long factor = 0x1; while (dividendLong >= (divisorLong << 1)) { divisorLong <<= 1; factor <<= 1; } while (factor > 0 && dividendLong > 0) { if(dividendLong >= divisorLong) { dividendLong -= divisorLong; re += factor; } factor >>>= 1; divisorLong >>>= 1; } return flag ? re : -re; } }
给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
示例 1:
输入:
s = "barfoothefoobarman",
words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:
输入:
s = "wordgoodgoodgoodbestword",
words = ["word","good","best","word"]
输出:[]
class Solution { public List<Integer> findSubstring(String s, String[] words) { List<Integer> re = new ArrayList<>(); if(s == null || words == null || words.length == 0 || words[0] == null) { return re; } if(s.length() == 0 || words[0].length() == 0 || s.length() < words.length * words[0].length()) { return re; } // 用< 单词,出现次数 > 来存储 words 中的元素,方便查找 HashMap<String,Integer> map = new HashMap(); for (String string : words) { map.put(string, map.getOrDefault(string,0) + 1); } int len = words[0].length(); int strLen = s.length(); int lastStart = len * words.length - len; for (int i = 0; i < len; i++) { for (int j = i; j <= strLen - len - lastStart; j += len) { String tempStr = s.substring(j, j + len); if(map.containsKey(tempStr)) { HashMap<String,Integer> searched = new HashMap<>(); // 从后向前依次对比 int tempIndex = j + lastStart; String matchedStr = s.substring(tempIndex, tempIndex + len); while (tempIndex >= j && map.containsKey(matchedStr)) { // 正确匹配到单词 if(searched.getOrDefault(matchedStr,0) < map.get(matchedStr)) { searched.put(matchedStr, searched.getOrDefault(matchedStr,0) + 1); } else { break; } tempIndex -= len; if(tempIndex < j) { break; } matchedStr = s.substring(tempIndex, tempIndex + len); } // 完全匹配所以单词 if(j > tempIndex) { re.add(j); } // 从tempIndex 到 tempIndex + len 这个单词不能正确匹配 else { j = tempIndex; } } } } return re; } }
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3
→ 1,3,2
3,2,1
→ 1,2,3
1,1,5
→ 1,5,1
class Solution { public void nextPermutation(int[] nums) { boolean flag = false; for (int i = nums.length - 2; i >= 0; --i) { if (nums[i] < nums[i + 1]) { int index = findMinIndex(nums, i, nums[i]); swap(nums, i, index); reverse(nums, i + 1); flag = true; break; } } if (!flag) { Arrays.sort(nums); } } private void reverse(int[] nums, int start) { int end = nums.length - 1; while (start < end) { swap(nums, start++, end--); } } /** * 找出从start开始的比val大的最小元素的下标,如果有多个,选择后者 * * @param name * @param start * @param val * @return index */ private int findMinIndex(int[] nums, int start, int val) { int end = nums.length - 1; int i = start; for (; i < end; ++i) { if (nums[i + 1] <= val) { break; } } return i; } private void swap(int[] nums, int i, int j) { int t = nums[i]; nums[i] = nums[j]; nums[j] = t; } }
给定一个只包含 '('
和 ')'
的字符串,找出最长的包含有效括号的子串的长度。
示例 1:
输入: "(()"
输出: 2
解释: 最长有效括号子串为 "()"
示例 2:
输入: ")()())
" 输出: 4 解释: 最长有效括号子串为"()()"
class Solution { public int longestValidParentheses(String s) { if (s == null || s.length() < 2) { return 0; } char[] chars = s.toCharArray(); int n = chars.length; int[] res = new int[n]; res[0] = 0; res[1] = chars[1] == ')' && chars[0] == '(' ? 2 : 0; int max = res[1]; for (int i = 2; i < n; ++i) { if (chars[i] == ')') { if (chars[i - 1] == '(') { res[i] = res[i - 2] + 2; } else { int index = i - res[i - 1] - 1; if (index >= 0 && chars[index] == '(') { // ()(()) res[i] = res[i - 1] + 2 + (index - 1 >= 0 ? res[index - 1] : 0); } } } max = Math.max(max, res[i]); } return max; } }
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7]
可能变为 [4,5,6,7,0,1,2]
)。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1
。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
示例 1:
输入: nums = [4,5,6,7,0,1,2]
, target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2]
, target = 3
输出: -1
class Solution { public int search(int[] A, int target) { if (A == null || A.length == 0) return -1; int low = 0,high = A.length - 1; while (low <= high) { int mid = low + ((high - low) >> 1); if (target < A[mid]) { if (A[mid] >= A[high] && target < A[low]) low = mid + 1; else high = mid - 1; } else if (target > A[mid]) { if (A[low] >= A[mid] && target > A[high]) high = mid - 1; else low = mid + 1; } else return mid; } return -1; } }
给定一个按照升序排列的整数数组 nums
,和一个目标值 target
。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]
。
示例 1:
输入: nums = [5,7,7,8,8,10]
, target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10]
, target = 6
输出: [-1,-1]
class Solution { public int[] searchRange(int[] nums, int target) { int start = 0, end = nums.length - 1; while (start <= end) { int mid = start + (end - start) / 2; if (nums[mid] < target) start = mid + 1; else if (nums[mid] > target) end = mid - 1; if (nums[mid] == target) { return new int[]{findFirst(nums, start, mid, target),findEnd(nums, mid, end, target)}; } } return new int[]{-1,-1}; } private int findFirst(int[] nums, int start, int end, int target) { while (start < end) { int temp = start + (end - start) / 2; if (nums[temp] < target) start = temp + 1; else if (nums[temp] == target) end = temp; } return start; } private int findEnd(int[] nums, int start, int end, int target) { while (start < end) { int temp = start + (end - start + 1) / 2; if (nums[temp] > target) end = temp - 1; else if (nums[temp] == target) start = temp; } return start; } }
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5 输出: 2
示例 2:
输入: [1,3,5,6], 2 输出: 1
示例 3:
输入: [1,3,5,6], 7 输出: 4
示例 4:
输入: [1,3,5,6], 0 输出: 0
二分查找。
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
l, h = 0, len(nums) - 1
while l <= h:
m = l + ((h - l) >> 1)
if nums[m] == target:
return m
if nums[m] < target:
l = m + 1
else:
h = m - 1
return l
class Solution {
public int searchInsert(int[] nums, int target) {
int l = 0, h = nums.length - 1;
while (l <= h) {
int m = l + ((h - l) >> 1);
if (nums[m] == target) return m;
if (nums[m] < target) l = m + 1;
else h = m - 1;
}
return l;
}
}
func searchInsert(nums []int, target int) int {
l, h := 0, len(nums) - 1
for l <= h {
m := l + ((h - l) >> 1)
if nums[m] == target {
return m
}
if nums[m] < target {
l = m + 1
} else {
h = m - 1
}
}
return l
}
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int l = 0, h = nums.size() - 1;
while (l <= h) {
int m = l + ((h - l) >> 1);
if (nums[m] == target) return m;
if (nums[m] < target) l = m + 1;
else h = m - 1;
}
return l;
}
};
/** * @param {number[]} nums * @param {number} target * @return {number} */ var searchInsert = function (nums, target) { let l = 0, h = nums.length; while (l <= h) { const m = l + ((h - l) >> 1); if (nums[m] == target) return m; if (nums[m] < target) l = m + 1; else h = m - 1; } return l; };
判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
1-9
在每一行只能出现一次。1-9
在每一列只能出现一次。1-9
在每一个以粗实线分隔的 3x3
宫内只能出现一次。上图是一个部分填充的有效的数独。
数独部分空格内已填入了数字,空白格用 '.'
表示。
示例 1:
输入: [ ["5","3",".",".","7",".",".",".","."], ["6",".",".","1","9","5",".",".","."], [".","9","8",".",".",".",".","6","."], ["8",".",".",".","6",".",".",".","3"], ["4",".",".","8",".","3",".",".","1"], ["7",".",".",".","2",".",".",".","6"], [".","6",".",".",".",".","2","8","."], [".",".",".","4","1","9",".",".","5"], [".",".",".",".","8",".",".","7","9"] ] 输出: true
示例 2:
输入: [ ["8","3",".",".","7",".",".",".","."], ["6",".",".","1","9","5",".",".","."], [".","9","8",".",".",".",".","6","."], ["8",".",".",".","6",".",".",".","3"], ["4",".",".","8",".","3",".",".","1"], ["7",".",".",".","2",".",".",".","6"], [".","6",".",".",".",".","2","8","."], [".",".",".","4","1","9",".",".","5"], [".",".",".",".","8",".",".","7","9"] ] 输出: false 解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
说明:
1-9
和字符 '.'
。9x9
形式的。
class Solution {
public boolean isValidSudoku(char[][] board) {
for(int i = 0; i < 9; i++) {
HashSet<Character> col = new HashSet<>() , row = new HashSet<>() , cube = new HashSet<>();
for(int j = 0; j < 9; j++) {
if(board[i][j] != '.' && !row.add(board[i][j])) return false;
if(board[j][i] != '.' && !col.add(board[j][i])) return false;
int colIndex = i/3*3+j/3 , rowIndex = i%3*3+j%3;
if(board[rowIndex][colIndex] != '.' && !cube.add(board[rowIndex][colIndex])) return false;
}
}
return true;
}
}
编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
1-9
在每一行只能出现一次。1-9
在每一列只能出现一次。1-9
在每一个以粗实线分隔的 3x3
宫内只能出现一次。空白格用 '.'
表示。
一个数独。
答案被标成红色。
Note:
1-9
和字符 '.'
。9x9
形式的。
class Solution { private char[][] board; private boolean[][] rows; private boolean[][] cols; private boolean[][] subBox; public void solveSudoku(char[][] board) { this.board = board; this.rows = new boolean[9][9]; this.cols = new boolean[9][9]; this.subBox = new boolean[9][9]; int val; for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { if (board[i][j] != '.') { val = board[i][j] - '0'; rows[i][val - 1] = true; cols[j][val - 1] = true; subBox[(i / 3) * 3 + j / 3][val - 1] = true; } } } fillSudoku(0, 0); } private boolean fillSudoku(int i, int j) { if (i == 9) return true; int x = i, y = j; if (y == 8) { x++; y = 0; } else y++; if (board[i][j] == '.') { for (int k = 1; k <= 9; k++) { if (!rows[i][k - 1] && !cols[j][k - 1] && !subBox[(i / 3) * 3 + j / 3][k - 1]) { rows[i][k - 1] = true; cols[j][k - 1] = true; subBox[(i / 3) * 3 + j / 3][k - 1] = true; if (fillSudoku(x, y)) { board[i][j] = (char) (k + '0'); return true; } rows[i][k - 1] = false; cols[j][k - 1] = false; subBox[(i / 3) * 3 + j / 3][k - 1] = false; } } return false; } else return fillSudoku(x, y); } }
「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。前五项如下:
1. 1 2. 11 3. 21 4. 1211 5. 111221
1
被读作 "one 1"
("一个一"
) , 即 11
。
11
被读作 "two 1s"
("两个一"
), 即 21
。
21
被读作 "one 2"
, "one 1"
("一个二"
, "一个一"
) , 即 1211
。
给定一个正整数 n(1 ≤ n ≤ 30),输出外观数列的第 n 项。
注意:整数序列中的每一项将表示为一个字符串。
示例 1:
输入: 1 输出: "1" 解释:这是一个基本样例。
示例 2:
输入: 4 输出: "1211" 解释:当 n = 3 时,序列是 "21",其中我们有 "2" 和 "1" 两组,"2" 可以读作 "12",也就是出现频次 = 1 而 值 = 2;类似 "1" 可以读作 "11"。所以答案是 "12" 和 "11" 组合在一起,也就是 "1211"。
class Solution { public String countAndSay(int n) { String one = "1"; while (n > 1) { one = say(one); n--; } return one; } private String say(String las) { StringBuilder sBuilder = new StringBuilder(); int l = 1; for (int i = 0; i < las.length(); i++) { if (i < las.length() - 1 && las.charAt(i) == las.charAt(i + 1)) l++; else { sBuilder.append(l).append(las.charAt(i)); l = 1; } } return sBuilder.toString(); } }
给定一个无重复元素的数组 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的数字可以无限制重复被选取。
说明:
target
)都是正整数。示例 1:
输入: candidates =[2,3,6,7],
target =7
, 所求解集为: [ [7], [2,2,3] ]
示例 2:
输入: candidates = [2,3,5],
target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
class Solution { public List<List<Integer>> combinationSum(int[] candidates, int target) { List<List<Integer>> result = new ArrayList<>(); Arrays.sort(candidates); combinationSum(candidates,target,candidates.length-1, new ArrayList<>(),result); return result; } private void combinationSum(int[] candidates, int target,int length, List<Integer> integers, List<List<Integer>> result) { List<Integer> list; for (int i = length; i >= 0; i--) { int nc = candidates[i]; if (nc>target) continue; list = new ArrayList<>(integers); list.add(nc); if (nc==target) result.add(list); else combinationSum(candidates, target - nc, i, list,result); } } }
给定一个数组 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的每个数字在每个组合中只能使用一次。
说明:
示例 1:
输入: candidates =[10,1,2,7,6,1,5]
, target =8
, 所求解集为: [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5, 所求解集为: [ [1,2,2], [5] ]
class Solution { private List<List<Integer>> result = new ArrayList<>(); public List<List<Integer>> combinationSum2(int[] candidates, int target) { Arrays.sort(candidates); combinationSum(candidates,target,candidates.length-1, new ArrayList<>()); return result; } private void combinationSum(int[] candidates, int target,int length, List<Integer> integers) { List<Integer> list; for (int i = length; i >= 0; i--) { int nc = candidates[i]; if (nc>target || i<length && nc==candidates[i+1]) continue; list = new ArrayList<>(integers); list.add(nc); if (nc==target) result.add(list); else combinationSum(candidates, target - nc, i-1, list); } } }
给定一个未排序的整数数组,找出其中没有出现的最小的正整数。
示例 1:
输入: [1,2,0] 输出: 3
示例 2:
输入: [3,4,-1,1] 输出: 2
示例 3:
输入: [7,8,9,11,12] 输出: 1
说明:
你的算法的时间复杂度应为O(n),并且只能使用常数级别的空间。
public class Solution { public int firstMissingPositive(int[] num) { for (int i = 0; i < num.length; i++) { if (num[i] > 0 && num[i] < num.length && num[num[i] - 1] != num[i]) { swap(num, i, num[i] - 1); i--; } } for (int i = 0; i < num.length; i++) { if (i + 1 != num[i]) { return i + 1; } } return num.length + 1; } private void swap(int[] num, int i, int j) { int temp = num[i]; num[i] = num[j]; num[j] = temp; } }
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1] 输出: 6
class Solution { public int trap(int[] height) { if (height == null || height.length == 0) return 0; int lx = 0, rx = height.length - 1, l = height[lx], r = height[rx], re = 0; while (lx < rx) { if (l < r) { lx++; if (height[lx] < l) re += l - height[lx]; else l = height[lx]; } else { rx--; if (height[rx] < r) re += r - height[rx]; else r = height[rx]; } } return re; } }
给定两个以字符串形式表示的非负整数 num1
和 num2
,返回 num1
和 num2
的乘积,它们的乘积也表示为字符串形式。
示例 1:
输入: num1 = "2", num2 = "3" 输出: "6"
示例 2:
输入: num1 = "123", num2 = "456" 输出: "56088"
说明:
num1
和 num2
的长度小于110。num1
和 num2
只包含数字 0-9
。num1
和 num2
均不以零开头,除非是数字 0 本身。
class Solution { public String multiply(String num1, String num2) { char[] chars1 = num1.toCharArray(),chars2 = num2.toCharArray(); int[] result = new int[chars1.length+chars2.length]; int pow = result.length-1; for (int i = chars1.length - 1; i >= 0; i--) { int a = chars1[i] - '0'; int j = 0,bit = pow; for (int i1 = chars2.length - 1; i1 >= 0; i1--) { int b = chars2[i1] -'0'; j = a*b + j + result[bit]; result[bit--] = j%10; j = j/10; } while (j!=0){ j += result[bit]; result[bit--] = j%10; j = j/10; } pow--; } StringBuilder builder = new StringBuilder(); int i = 0; for (; i < result.length; i++) if (result[i] != 0) break; for (; i < result.length; i++) builder.append(result[i]); return builder.length()==0? "0" : builder.toString(); } }
给定一个字符串 (s
) 和一个字符模式 (p
) ,实现一个支持 '?'
和 '*'
的通配符匹配。
'?' 可以匹配任何单个字符。 '*' 可以匹配任意字符串(包括空字符串)。
两个字符串完全匹配才算匹配成功。
说明:
s
可能为空,且只包含从 a-z
的小写字母。p
可能为空,且只包含从 a-z
的小写字母,以及字符 ?
和 *
。示例 1:
输入: s = "aa" p = "a" 输出: false 解释: "a" 无法匹配 "aa" 整个字符串。
示例 2:
输入: s = "aa" p = "*" 输出: true 解释: '*' 可以匹配任意字符串。
示例 3:
输入: s = "cb" p = "?a" 输出: false 解释: '?' 可以匹配 'c', 但第二个 'a' 无法匹配 'b'。
示例 4:
输入: s = "adceb" p = "*a*b" 输出: true 解释: 第一个 '*' 可以匹配空字符串, 第二个 '*' 可以匹配字符串 "dce".
示例 5:
输入: s = "acdcb" p = "a*c?b" 输入: false
class Solution { public boolean isMatch(String s, String p) { int i = 0, j = 0, is = -1, ip = -1; char[] ss = s.toCharArray(); char[] pp = p.toCharArray(); while (i < ss.length) { if (j < pp.length && (ss[i] == pp[j] || pp[j] == '?')) { i++; j++; } else if (j < pp.length && pp[j] == '*') { ip = j++; is = i; } else if (ip != -1) { j = ip + 1; i = ++is; } else return false; } while (j < pp.length && pp[j] == '*') j++; return j == p.length(); } }
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例:
输入: [2,3,1,1,4] 输出: 2 解释: 跳到最后一个位置的最小跳跃数是2
。 从下标为 0 跳到下标为 1 的位置,跳1
步,然后跳3
步到达数组的最后一个位置。
说明:
假设你总是可以到达数组的最后一个位置。
class Solution {
public int jump(int[] nums) {
int cnt = 0,last = 0, next = 1;
for (;next < nums.length;cnt++){
int i = last;
last = next;
for (; i < last; ++i) if (i + nums[i] >= next) next = i + nums[i] + 1;
}
return cnt;
}
}
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
class Solution { public List<List<Integer>> permute(int[] nums) { List<List<Integer>> list = new ArrayList<>(); permute(list, nums, 0); return list; } private void permute(List<List<Integer>> list, int[] nums, int start) { int end = nums.length - 1; if (start == end) { list.add(Arrays.stream(nums).boxed().collect(Collectors.toList())); return; } for (int i = start; i <= end; ++i) { swap(nums, i, start); permute(list, nums, start + 1); swap(nums, i, start); } } private static void swap(int[] nums, int i, int j) { int t = nums[i]; nums[i] = nums[j]; nums[j] = t; } }
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2] 输出: [ [1,1,2], [1,2,1], [2,1,1] ]
class Solution { public List<List<Integer>> permuteUnique(int[] nums) { List<List<Integer>> list = new ArrayList<>(); permute(list, nums, 0); return list; } private void permute(List<List<Integer>> list, int[] nums, int start) { int end = nums.length - 1; if (start == end) { List<Integer> tmp = new ArrayList<>(); for (int val : nums) { tmp.add(val); } list.add(tmp); } for (int i = start; i <= end; ++i) { if (isSwap(nums, start, i)) { swap(nums, i, start); permute(list, nums, start + 1); swap(nums, i, start); } } } private boolean isSwap(int[] nums, int from, int to) { for (int i = from; i < to; ++i) { if (nums[i] == nums[to]) { // 相等,不进行交换 return false; } } return true; } private void swap(int[] nums, int i, int j) { int t = nums[i]; nums[i] = nums[j]; nums[j] = t; } }
给定一个 n × n 的二维矩阵表示一个图像。
将图像顺时针旋转 90 度。
说明:
你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。
示例 1:
给定 matrix = [ [1,2,3], [4,5,6], [7,8,9] ], 原地旋转输入矩阵,使其变为: [ [7,4,1], [8,5,2], [9,6,3] ]
示例 2:
给定 matrix = [ [ 5, 1, 9,11], [ 2, 4, 8,10], [13, 3, 6, 7], [15,14,12,16] ], 原地旋转输入矩阵,使其变为: [ [15,13, 2, 5], [14, 3, 4, 1], [12, 6, 8, 9], [16, 7,10,11] ]
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
s, n = 0, len(matrix)
while s < (n >> 1):
e = n - s - 1
for i in range(s, e):
t = matrix[i][e]
matrix[i][e] = matrix[s][i]
matrix[s][i] = matrix[n - i - 1][s]
matrix[n - i - 1][s] = matrix[e][n - i - 1]
matrix[e][n - i - 1] = t
s += 1
class Solution { public void rotate(int[][] matrix) { int s = 0, n = matrix.length; while (s < (n >> 1)) { int e = n - s - 1; for (int i = s; i < e; ++i) { int t = matrix[i][e]; matrix[i][e] = matrix[s][i]; matrix[s][i] = matrix[n - i - 1][s]; matrix[n - i - 1][s] = matrix[e][n - i - 1]; matrix[e][n - i - 1] = t; } ++s; } } }
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"]
,
输出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
说明:
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
Map<String, List<String>> map = new HashMap<>();
for (String str : strs) {
char[] chars = str.toCharArray();
Arrays.sort(chars);
String key = new String(chars);
if (!map.containsKey(key)) map.put(key, new ArrayList<>());
map.get(key).add(str);
}
return new ArrayList<>(map.values());
}
}
实现 pow(x, n) ,即计算 x 的 n 次幂函数。
示例 1:
输入: 2.00000, 10 输出: 1024.00000
示例 2:
输入: 2.10000, 3 输出: 9.26100
示例 3:
输入: 2.00000, -2 输出: 0.25000 解释: 2-2 = 1/22 = 1/4 = 0.25
说明:
class Solution {
public double myPow(double x, int n) {
if(n < 0) return sum(1.0 / x,0 - n);
return sum(x,n);
}
public double sum(double x, int n) {
if(n == 0) return 1;
if(n == 1) return x;
if( n % 2 == 0) return sum(x * x, n / 2);
else return x * sum(x * x, n / 2);
}
}
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
上图为 8 皇后问题的一种解法。
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q'
和 '.'
分别代表了皇后和空位。
示例:
输入: 4 输出: [ [".Q..", // 解法 1 "...Q", "Q...", "..Q."], ["..Q.", // 解法 2 "Q...", "...Q", ".Q.."] ] 解释: 4 皇后问题存在两个不同的解法。
class Solution { private List<List<String>> solutions; private char[][] nQueens; private boolean[] colUsed; private boolean[] diagonals45Used; private boolean[] diagonals135Used; private int n; public List<List<String>> solveNQueens(int n) { solutions = new ArrayList<>(); nQueens = new char[n][n]; for (int i = 0; i < n; i++) Arrays.fill(nQueens[i], '.'); colUsed = new boolean[n]; diagonals45Used = new boolean[(2 * n) - 1]; diagonals135Used = new boolean[(2 * n) - 1]; this.n = n; backtracking(0); return solutions; } private void backtracking(int row) { if (row == n) { List<String> list = new ArrayList<>(); for (char[] chars : nQueens) list.add(new String(chars)); solutions.add(list); return; } for (int col = 0; col < n; col++) { int diagonals45Idx = row + col; int diagonals135Idx = n - 1 - (row - col); if (colUsed[col] || diagonals45Used[diagonals45Idx] || diagonals135Used[diagonals135Idx]) continue; nQueens[row][col] = 'Q'; colUsed[col] = diagonals45Used[diagonals45Idx] = diagonals135Used[diagonals135Idx] = true; backtracking(row + 1); colUsed[col] = diagonals45Used[diagonals45Idx] = diagonals135Used[diagonals135Idx] = false; nQueens[row][col] = '.'; } } }
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
上图为 8 皇后问题的一种解法。
给定一个整数 n,返回 n 皇后不同的解决方案的数量。
示例:
输入: 4 输出: 2 解释: 4 皇后问题存在如下两个不同的解法。 [ [".Q..", // 解法 1 "...Q", "Q...", "..Q."], ["..Q.", // 解法 2 "Q...", "...Q", ".Q.."] ]
class Solution { int count = 0; public int totalNQueens(int n) { int[] c = new int[n]; search(0, n, c); return count; } public void search(int cur, int n, int[] c) { if (cur == n) { count++; return; } for (int i = 0; i < n; i++) { boolean flag = true; c[cur] = i; for (int j = 0; j < cur; j++) { if ((c[cur] == c[j]) || ((c[cur] - cur) == (c[j] - j)) || ((c[cur] + cur) == (c[j] + j))) { flag = false; break; } } if (flag) search(cur + 1, n, c); } } }
给定一个整数数组 nums
,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
设 dp[i] 表示 [0..i]
中,以 nums[i]
结尾的最大子数组和,状态转移方程 dp[i] = nums[i] + max(dp[i - 1], 0)
。
由于 dp[i]
只与子问题 dp[i-1]
有关,故可以用一个变量 f 来表示。
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
n = len(nums)
res = f = nums[0]
for i in range(1, n):
f = nums[i] + max(f, 0)
res = max(res, f)
return res
class Solution {
public int maxSubArray(int[] nums) {
int f = nums[0], res = nums[0];
for (int i = 1, n = nums.length; i < n; ++i) {
f = nums[i] + Math.max(f, 0);
res = Math.max(res, f);
}
return res;
}
}
给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。
示例 1:
输入: [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ] 输出: [1,2,3,6,9,8,7,4,5]
示例 2:
输入: [ [1, 2, 3, 4], [5, 6, 7, 8], [9,10,11,12] ] 输出: [1,2,3,4,8,12,11,10,9,5,6,7]
提示:
从外往里一圈一圈遍历并存储矩阵元素即可。
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
def add(i1, j1, i2, j2):
if i1 == i2:
return [matrix[i1][j] for j in range(j1, j2 + 1)]
if j1 == j2:
return [matrix[i][j1] for i in range(i1, i2 + 1)]
return [matrix[i1][j] for j in range(j1, j2)] + [matrix[i][j2] for i in range(i1, i2)] + [matrix[i2][j] for j in range(j2, j1, -1)] + [matrix[i][j1] for i in range(i2, i1, -1)]
m, n = len(matrix), len(matrix[0])
i1, j1, i2, j2 = 0, 0, m - 1, n - 1
res = []
while i1 <= i2 and j1 <= j2:
res += add(i1, j1, i2, j2)
i1, j1, i2, j2 = i1 + 1, j1 + 1, i2 - 1, j2 - 1
return res
class Solution { private List<Integer> res; public List<Integer> spiralOrder(int[][] matrix) { int m = matrix.length, n = matrix[0].length; res = new ArrayList<>(); int i1 = 0, i2 = m - 1; int j1 = 0, j2 = n - 1; while (i1 <= i2 && j1 <= j2) { add(matrix, i1++, j1++, i2--, j2--); } return res; } private void add(int[][] matrix, int i1, int j1, int i2, int j2) { if (i1 == i2) { for (int j = j1; j <= j2; ++j) { res.add(matrix[i1][j]); } return; } if (j1 == j2) { for (int i = i1; i <= i2; ++i) { res.add(matrix[i][j1]); } return; } for (int j = j1; j < j2; ++j) { res.add(matrix[i1][j]); } for (int i = i1; i < i2; ++i) { res.add(matrix[i][j2]); } for (int j = j2; j > j1; --j) { res.add(matrix[i2][j]); } for (int i = i2; i > i1; --i) { res.add(matrix[i][j1]); } } }
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
示例 1:
输入: [2,3,1,1,4] 输出: true 解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
示例 2:
输入: [3,2,1,0,4] 输出: false 解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。
class Solution {
public boolean canJump(int[] nums) {
int count = 0;
for (int i = nums.length - 2; i >= 0; i --) {
count = nums[i] > count ? 0 : count + 1;
}
return count == 0;
}
}
给出一个区间的集合,请合并所有重叠的区间。
示例 1:
输入: [[1,3],[2,6],[8,10],[15,18]] 输出: [[1,6],[8,10],[15,18]] 解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入: [[1,4],[4,5]] 输出: [[1,5]] 解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
/** * Definition for an interval. * public class Interval { * int start; * int end; * Interval() { start = 0; end = 0; } * Interval(int s, int e) { start = s; end = e; } * } */ class Solution { public List<Interval> merge(List<Interval> intervals) { int n=intervals.size(); int[] starts=new int[n],ends=new int[n]; for(int i=0;i<n;i++){ starts[i]=intervals.get(i).start; ends[i]=intervals.get(i).end; } Arrays.sort(starts); Arrays.sort(ends); List<Interval> res= new ArrayList<>(); for(int i=0,j=0;i<n;i++){ if((i == (n - 1)) || (starts[i + 1] > ends[i])){ res.add(new Interval(starts[j],ends[i])); j=i+1; } } return res; } }
给出一个无重叠的 ,按照区间起始端点排序的区间列表。
在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。
示例 1:
输入: intervals = [[1,3],[6,9]], newInterval = [2,5] 输出: [[1,5],[6,9]]
示例 2:
输入: intervals =[[1,2],[3,5],[6,7],[8,10],[12,16]]
, newInterval =[4,8]
输出: [[1,2],[3,10],[12,16]] 解释: 这是因为新的区间[4,8]
与[3,5],[6,7],[8,10]
重叠。
/** * Definition for an interval. * public class Interval { * int start; * int end; * Interval() { start = 0; end = 0; } * Interval(int s, int e) { start = s; end = e; } * } */ class Solution { public List<Interval> insert(List<Interval> intervals, Interval newInterval) { List<Interval> list = new LinkedList<>(); int i = 0; while ((i < intervals.size()) && (intervals.get(i).end < newInterval.start)) list.add(intervals.get(i++)); while ((i < intervals.size()) && (intervals.get(i).start <= newInterval.end)) { newInterval = new Interval( Math.min(intervals.get(i).start, newInterval.start), Math.max(intervals.get(i).end, newInterval.end) ); i++; } list.add(newInterval); while (i < intervals.size()) list.add(intervals.get(i++)); return list; } }
给定一个仅包含大小写字母和空格 ' '
的字符串 s
,返回其最后一个单词的长度。如果字符串从左向右滚动显示,那么最后一个单词就是最后出现的单词。
如果不存在最后一个单词,请返回 0 。
说明:一个单词是指仅由字母组成、不包含任何空格字符的 最大子字符串。
示例:
输入: "Hello World" 输出: 5
class Solution:
def lengthOfLastWord(self, s: str) -> int:
last_word_length = 0
meet_word = False
for i in range(len(s) - 1, -1, -1):
ch = ord(s[i])
if ch >= 65 and ch <= 122:
meet_word = True
last_word_length += 1
elif meet_word:
break
return last_word_length
class Solution { public int lengthOfLastWord(String s) { int n = s.length(); int lastWordLength = 0; boolean meetWord = false; for (int i = n - 1; i >= 0; --i) { char ch = s.charAt(i); if (ch >= 'A' && ch <= 'z') { meetWord = true; ++lastWordLength; } else if (meetWord) { break; } } return lastWordLength; } }
给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
class Solution: def generateMatrix(self, n: int) -> List[List[int]]: res = [[0] * n for _ in range(n)] num = 1 m1, m2 = 0, n - 1 while m1 < m2: for j in range(m1, m2): res[m1][j] = num num += 1 for i in range(m1, m2): res[i][m2] = num num += 1 for j in range(m2, m1, -1): res[m2][j] = num num += 1 for i in range(m2, m1, -1): res[i][m1] = num num += 1 m1 += 1 m2 -= 1 if m1 == m2: res[m1][m1] = num return res
class Solution { public int[][] generateMatrix(int n) { int[][] res = new int[n][n]; int num = 1; int m1 = 0, m2 = n - 1; while (m1 < m2) { for (int j = m1; j < m2; ++j) { res[m1][j] = num++; } for (int i = m1; i < m2; ++i) { res[i][m2] = num++; } for (int j = m2; j > m1; --j) { res[m2][j] = num++; } for (int i = m2; i > m1; --i) { res[i][m1] = num++; } ++m1; --m2; } if (m1 == m2) { res[m1][m1] = num; } return res; } }
class Solution { public: vector<vector<int>> generateMatrix(int n) { vector<vector<int>> res(n, vector<int>(n, 0)); int num = 1; int m1 = 0, m2 = n - 1; while (m1 < m2) { for (int j = m1; j < m2; ++j) { res[m1][j] = num++; } for (int i = m1; i < m2; ++i) { res[i][m2] = num++; } for (int j = m2; j > m1; --j) { res[m2][j] = num++; } for (int i = m2; i > m1; --i) { res[i][m1] = num++; } ++m1; --m2; } if (m1 == m2) { res[m1][m1] = num; } return res; } };
给出集合 [1,2,3,…,n]
,其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
"123"
"132"
"213"
"231"
"312"
"321"
给定 n 和 k,返回第 k 个排列。
说明:
示例 1:
输入: n = 3, k = 3 输出: "213"
示例 2:
输入: n = 4, k = 9 输出: "2314"
class Solution { public String getPermutation(int n, int k) { StringBuilder s = new StringBuilder(); int[] fact = new int[n]; fact[0] = 1; for (int i = 1; i < n; i++) fact[i] = fact[i - 1] * i; List<Integer> list = new ArrayList<>(); for (int i = 1; i <= n; i++) list.add(i); k--; for (int i = n; i >= 1; i--) { int j=k/fact[i-1]; k=k%fact[i-1]; s.append(list.get(j)); list.remove(j); } return s.toString(); } }
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: 1->2->3->4->5->NULL, k = 2 输出: 4->5->1->2->3->NULL 解释: 向右旋转 1 步: 5->1->2->3->4->NULL 向右旋转 2 步: 4->5->1->2->3->NULL
示例 2:
输入: 0->1->2->NULL, k = 4 输出:2->0->1->NULL
解释: 向右旋转 1 步: 2->0->1->NULL 向右旋转 2 步: 1->2->0->NULL 向右旋转 3 步:0->1->2->NULL
向右旋转 4 步:2->0->1->NULL
将链表右半部分的 k 的节点拼接到 head 即可。
注:k 对链表长度 n 取余,即 k %= n
。
# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: def rotateRight(self, head: ListNode, k: int) -> ListNode: if head is None or head.next is None or k == 0: return head n = 0 cur = head while cur: n += 1 cur = cur.next k %= n if k == 0: return head p = q = head for i in range(k): q = q.next while q.next: p, q = p.next, q.next start = p.next p.next = None q.next = head return start
/** * 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 rotateRight(ListNode head, int k) { if (head == null || head.next == null) { return head; } int n = 0; ListNode cur = head; while (cur != null) { ++n; cur = cur.next; } k %= n; if (k == 0) { return head; } ListNode p = head, q = head; for (int i = 0; i < k; ++i) { q = q.next; } while (q.next != null) { p = p.next; q = q.next; } ListNode start = p.next; p.next = null; q.next = head; return start; } }
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
例如,上图是一个7 x 3 的网格。有多少可能的路径?
示例 1:
输入: m = 3, n = 2 输出: 3 解释: 从左上角开始,总共有 3 条路径可以到达右下角。 1. 向右 -> 向右 -> 向下 2. 向右 -> 向下 -> 向右 3. 向下 -> 向右 -> 向右
示例 2:
输入: m = 7, n = 3 输出: 28
提示:
1 <= m, n <= 100
2 * 10 ^ 9
class Solution { public int uniquePaths(int m, int n) { int[][] res = new int[n][m]; for (int i = 0; i < m; ++i) { res[0][i] = 1; } for (int i = 1; i < n; ++i) { res[i][0] = 1; } for (int i = 1; i < n; ++i) { for (int j = 1; j < m; ++j) { res[i][j] = res[i - 1][j] + res[i][j - 1]; } } return res[n - 1][m - 1]; } }
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1
和 0
来表示。
说明:m 和 n 的值均不超过 100。
示例 1:
输入:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2
条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
func uniquePathsWithObstacles(obstacleGrid [][]int) int { m,n := len(obstacleGrid),len(obstacleGrid[0]) dp := make([][]int,m) for i:=0; i < m;i++ { dp[i] = make([]int,n) } for i := 0; i < m; i++ { for j := 0; j < n; j++ { if obstacleGrid[i][j] == 0 { if i == 0 && j == 0 { dp[i][j] = 1 } else if i > 0 && j >0 { dp[i][j] = dp[i][j-1]+dp[i-1][j] } else if i > 0 { dp[i][j] = dp[i-1][j] } else { dp[i][j] = dp[i][j-1] } } } } return dp[m-1][n-1] } <!-- tabs:start --> ### **Python3** <!-- 这里可写当前语言的特殊实现逻辑 --> ```python
class Solution { public int uniquePathsWithObstacles(int[][] obstacleGrid) { int column = obstacleGrid[0].length, row = obstacleGrid.length; int[][] dp = new int[row][column]; // 第一行 for (int i = 0; i < column; i++) { if (obstacleGrid[0][i] == 1) { break; } dp[0][i] = 1; } // 第一列 for (int i = 0; i < row; i++) { if (obstacleGrid[i][0] == 1) { break; } dp[i][0] = 1; } // dp for (int i = 1; i < row; i++) { for (int j = 1; j < column; j++) { dp[i][j] = obstacleGrid[i][j] == 1 ? 0 : dp[i - 1][j] + dp[i][j - 1]; } } return dp[row - 1][column - 1]; } }
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例:
输入: [ [1,3,1], [1,5,1], [4,2,1] ] 输出: 7 解释: 因为路径 1→3→1→1→1 的总和最小。
class Solution {
public int minPathSum(int[][] grid) {
for (int i = 1; i < grid.length; i++) grid[i][0] += grid[i - 1][0];
for (int j = 1; j < grid[0].length; j++) grid[0][j] += grid[0][j - 1];
for (int i = 1; i < grid.length; i++)
for (int j = 1; j < grid[0].length; j++) grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]);
return grid[grid.length-1][grid[0].length-1];
}
}
验证给定的字符串是否可以解释为十进制数字。
例如:
"0"
=> true
" 0.1 "
=> true
"abc"
=> false
"1 a"
=> false
"2e10"
=> true
" -90e3 "
=> true
" 1e"
=> false
"e3"
=> false
" 6e-1"
=> true
" 99e2.5 "
=> false
"53.5e93"
=> true
" --6 "
=> false
"-+3"
=> false
"95a54e53"
=> false
说明: 我们有意将问题陈述地比较模糊。在实现代码之前,你应当事先思考所有可能的情况。这里给出一份可能存在于有效十进制数字中的字符列表:
当然,在输入中,这些字符的上下文也很重要。
更新于 2015-02-10:
C++
函数的形式已经更新了。如果你仍然看见你的函数接收 const char *
类型的参数,请点击重载按钮重置你的代码。
class Solution { public boolean isNumber(String s) { if(null==s || 0==s.length()) return false; int start=0,end=s.length()-1; char[] c=s.toCharArray(); while(start<=end && ' '==c[start]) start++; while(end>=start && ' '==c[end] ) end--; if(start>end) return false; if('+'==c[start] || '-'==c[start]) start++; boolean hasNum=false; boolean hasDot=false; boolean hasE=false; while(start<=end){ if(c[start]>='0' && c[start]<='9') hasNum = true; else if(c[start]=='e'){ if(hasE || !hasNum) return false; hasE=true; hasNum=false; } else if(c[start]=='.'){ if(hasDot || hasE) return false; hasDot=true; } else if('+'==c[start] || '-'==c[start]){ if(c[start-1]!='e') return false; }else return false; start++; } return hasNum; } }
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入: [1,2,3] 输出: [1,2,4] 解释: 输入数组表示数字 123。
示例 2:
输入: [4,3,2,1] 输出: [4,3,2,2] 解释: 输入数组表示数字 4321。
class Solution {
public int[] plusOne(int[] digits) {
for (int i = digits.length - 1; i >= 0; i --) {
digits[i] ++;
digits[i] = digits[i] % 10;
if (digits[i] != 0) return digits;
}
digits = new int[digits.length + 1];
digits[0] = 1;
return digits;
}
}
给定两个二进制字符串,返回他们的和(用二进制表示)。
输入为非空字符串且只包含数字 1
和 0
。
示例 1:
输入: a = "11", b = "1" 输出: "100"
示例 2:
输入: a = "1010", b = "1011" 输出: "10101"
class Solution { public String addBinary(String a, String b) { StringBuilder reverseAnswer = new StringBuilder(); int maxLength = Math.max(a.length(), b.length()); int carry = 0; for (int i = 0;i < maxLength;i++) { carry += i < a.length() ? a.charAt(a.length() - 1 - i) - 48 : 0; carry += i < b.length() ? b.charAt(b.length() - 1 - i) - 48 : 0; reverseAnswer.append(carry % 2); carry /= 2; } if (carry == 1) { reverseAnswer.append(1); } return reverseAnswer.reverse().toString(); } }
给定一个单词数组和一个长度 maxWidth,重新排版单词,使其成为每行恰好有 maxWidth 个字符,且左右两端对齐的文本。
你应该使用“贪心算法”来放置给定的单词;也就是说,尽可能多地往每行中放置单词。必要时可用空格 ' '
填充,使得每行恰好有 maxWidth 个字符。
要求尽可能均匀分配单词间的空格数量。如果某一行单词间的空格不能均匀分配,则左侧放置的空格数要多于右侧的空格数。
文本的最后一行应为左对齐,且单词之间不插入额外的空格。
说明:
words
至少包含一个单词。示例:
输入: words = ["This", "is", "an", "example", "of", "text", "justification."] maxWidth = 16 输出: [ "This is an", "example of text", "justification. " ]
示例 2:
输入: words = ["What","must","be","acknowledgment","shall","be"] maxWidth = 16 输出: [ "What must be", "acknowledgment ", "shall be " ] 解释: 注意最后一行的格式应为 "shall be " 而不是 "shall be", 因为最后一行应为左对齐,而不是左右两端对齐。 第二行同样为左对齐,这是因为这行只包含一个单词。
示例 3:
输入: words = ["Science","is","what","we","understand","well","enough","to","explain", "to","a","computer.","Art","is","everything","else","we","do"] maxWidth = 20 输出: [ "Science is what we", "understand well", "enough to explain to", "a computer. Art is", "everything else we", "do " ]
class Solution { public List<String> fullJustify(String[] words, int maxWidth) { ArrayList<String> res = new ArrayList<>(); if (words == null || words.length == 0) return res; int count = 0, last = 0; for (int i = 0; i < words.length; i++) { if (count + words[i].length() + (i - last) > maxWidth) { int spaceNum = 0, extraNum = 0; if (i - last - 1 > 0) { spaceNum = (maxWidth - count) / (i - last - 1); extraNum = (maxWidth - count) % (i - last - 1); } StringBuilder str = new StringBuilder(); for (int j = last; j < i; j++) { str.append(words[j]); if (j < i - 1) { for (int k = 0; k < spaceNum; k++) str.append(" "); if (extraNum > 0) str.append(" "); extraNum--; } } for (int j = str.length(); j < maxWidth; j++) str.append(" "); res.add(str.toString()); count = 0; last = i; } count += words[i].length(); } StringBuilder str = new StringBuilder(); for (int i = last; i < words.length; i++) { str.append(words[i]); if (str.length() < maxWidth) str.append(" "); } for (int i = str.length(); i < maxWidth; i++) str.append(" "); res.add(str.toString()); return res; } }
实现 int sqrt(int x)
函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
示例 1:
输入: 4 输出: 2
示例 2:
输入: 8 输出: 2 说明: 8 的平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。
class Solution {
public int mySqrt(int x) {
if(x==0)return 0;
long i=x;
while(i>x/i) i = (i + x / i) / 2;
return (int)i;
}
}
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2 输出: 2 解释: 有两种方法可以爬到楼顶。 1. 1 阶 + 1 阶 2. 2 阶
示例 2:
输入: 3 输出: 3 解释: 有三种方法可以爬到楼顶。 1. 1 阶 + 1 阶 + 1 阶 2. 1 阶 + 2 阶 3. 2 阶 + 1 阶
class Solution {
public int climbStairs(int n) {
if (n < 3) {
return n;
}
int[] res = new int[n + 1];
res[1] = 1;
res[2] = 2;
for (int i = 3; i < n + 1; ++i) {
res[i] = res[i - 1] + res[i - 2];
}
return res[n];
}
}
以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。
在 Unix 风格的文件系统中,一个点(.
)表示当前目录本身;此外,两个点 (..
) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs 相对路径
请注意,返回的规范路径必须始终以斜杠 /
开头,并且两个目录名之间必须只有一个斜杠 /
。最后一个目录名(如果存在)不能以 /
结尾。此外,规范路径必须是表示绝对路径的最短字符串。
示例 1:
输入:"/home/" 输出:"/home" 解释:注意,最后一个目录名后面没有斜杠。
示例 2:
输入:"/../" 输出:"/" 解释:从根目录向上一级是不可行的,因为根是你可以到达的最高级。
示例 3:
输入:"/home//foo/" 输出:"/home/foo" 解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。
示例 4:
输入:"/a/./b/../../c/" 输出:"/c"
示例 5:
输入:"/a/../../b/../c//.//" 输出:"/c"
示例 6:
输入:"/a//bc/d//././/.." 输出:"/a/b/c"
class Solution { public String simplifyPath(String path) { List<String> dirs = new ArrayList<>(); int dirStart = 0, len = path.length(); while (dirStart < len) { while (dirStart < len && path.charAt(dirStart) == '/') dirStart++; int dirEnd = dirStart; while (dirEnd < len && path.charAt(dirEnd) != '/') dirEnd++; String dir = path.substring(dirStart, dirEnd); if (!".".equals(dir)) { if ("..".equals(dir)) { if (! dirs.isEmpty()) dirs.remove(dirs.size() - 1); } else if (dir.length() > 0) { dirs.add(dir); } } dirStart = ++dirEnd; } StringBuilder sb = new StringBuilder("/"); for (int i = 0; i < dirs.size(); i++) { if (i == dirs.size() - 1) sb.append(dirs.get(i)); else sb.append(dirs.get(i)).append("/"); } return sb.toString(); } }
给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
示例 1:
输入: word1 = "horse", word2 = "ros" 输出: 3 解释: horse -> rorse (将 'h' 替换为 'r') rorse -> rose (删除 'r') rose -> ros (删除 'e')
示例 2:
输入: word1 = "intention", word2 = "execution" 输出: 5 解释: intention -> inention (删除 't') inention -> enention (将 'i' 替换为 'e') enention -> exention (将 'n' 替换为 'x') exention -> exection (将 'n' 替换为 'c') exection -> execution (插入 'u')
class Solution {
public int minDistance(String word1, String word2) {
return edit(word1.length(),word2.length(),word1,word2,new int[word1.length()+1][word2.length()+1]);
}
private int edit(int l, int r, String w1, String w2, int[][] dp){
if(l==0) return r;
if(r==0) return l;
if(dp[l][r]!=0) return dp[l][r];
int min = (w1.charAt(l-1)!=w2.charAt(r-1)) ?
Math.min(edit(l-1,r-1,w1,w2,dp)+1,Math.min(edit(l-1,r,w1,w2,dp)+1,edit(l,r-1,w1,w2,dp)+1))
: edit(l - 1, r - 1, w1, w2, dp);
dp[l][r] = min;
return min;
}
}
给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。
示例 1:
输入: [ [1,1,1], [1,0,1], [1,1,1] ] 输出: [ [1,0,1], [0,0,0], [1,0,1] ]
示例 2:
输入: [ [0,1,2,0], [3,4,5,2], [1,3,1,5] ] 输出: [ [0,0,0,0], [0,4,5,0], [0,3,1,0] ]
进阶:
class Solution { public void setZeroes(int[][] matrix) { int matrixRow = matrix.length, matrixCol = matrix[0].length; boolean[] row = new boolean[matrixRow], col = new boolean[matrixCol]; for (int i = 0; i < matrixRow; i++) { for (int j = 0; j < matrixCol; j++) { if (matrix[i][j] == 0) { row[i] = true; col[j] = true; } } } for (int i = 0; i < matrixRow; i++) { if (row[i]) for (int k = 0; k < matrixCol; k++) matrix[i][k] = 0; } for (int j = 0; j < matrixCol; j++) { if (col[j]) for (int k = 0; k < matrixRow; k++) matrix[k][j] = 0; } } }
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
示例 1:
输入: matrix = [ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50] ] target = 3 输出: true
示例 2:
输入: matrix = [ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50] ] target = 13 输出: false
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int x = 0,y = matrix.length-1;
if(y<0) return false;
int maxX = matrix[0].length-1;
while ((x <= maxX) && (y >= 0)) {
int cur = matrix[y][x];
if (cur == target) return false;
if (cur < target) x++;
else y--;
}
return false;
}
}
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意:
不能使用代码库中的排序函数来解决这道题。
示例:
输入: [2,0,2,1,1,0] 输出: [0,0,1,1,2,2]
进阶:
class Solution { public void sortColors(int[] nums) { int p = -1; int q = nums.length; int cur = 0; while (cur < q) { if (nums[cur] == 0) { swap(nums, cur++, ++p); } else if (nums[cur] == 1) { ++cur; } else { swap(nums, --q, cur); } } } private void swap(int[] nums, int i, int j) { int t = nums[i]; nums[i] = nums[j]; nums[j] = t; } }
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC" 输出: "BANC"
说明:
""
。
class Solution { public String minWindow(String s, String t) { int[] count = new int['z' - 'A' + 1]; int uniq = 0; for (int i = 0; i < t.length(); ++i) { if (++count[t.charAt(i) - 'A'] == 1) uniq++; } int found = 0,i = 0,j = 0; int minLen = Integer.MAX_VALUE; int minJ = Integer.MAX_VALUE; while (found < uniq) { while (i < s.length()) { if (found >= uniq) break; if (--count[s.charAt(i) - 'A'] == 0) found++; i++; } if (found < uniq) break; while (j < i && count[s.charAt(j) - 'A'] < 0) count[s.charAt(j++) - 'A']++; if (i - j < minLen) { minLen = i - j; minJ = j; } count[s.charAt(j++) - 'A']++; found--; } return minLen < Integer.MAX_VALUE ? s.substring(minJ, minJ + minLen) : ""; } }
给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例:
输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
class Solution { private List<List<Integer>> result; public List<List<Integer>> combine(int n, int k) { result = new ArrayList<>(); combine(n-k+1,k,0,1,new Integer[k]); return result; } private void combine(int n, int k, int i, int start, Integer[] list) { if (i==k) { result.add(new ArrayList<>(Arrays.asList(list))); return; } for (int j = start; j <= n+i; j++) { list[i] = j; combine(n,k,i+1,j+1,list); } } }
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
class Solution { public List<List<Integer>> subsets(int[] nums) { List<List<Integer>> list = new ArrayList<>(); for (int num : nums) { int size = list.size(); for (int j = 0; j < size; j++) { List<Integer> temp = new ArrayList<>(list.get(j)); temp.add(num); list.add(temp); } List<Integer> one = new ArrayList<>(); one.add(num); list.add(one); } list.add(new ArrayList<>()); return list; } }
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定 nums = [1,1,1,2,2,3], 函数应返回新长度 length =5
, 并且原数组的前五个元素被修改为
1, 1, 2, 2,
3 。 你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,1,2,3,3], 函数应返回新长度 length =7
, 并且原数组的前五个元素被修改为
0
, 0, 1, 1, 2, 3, 3 。 你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 int len = removeDuplicates(nums); // 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。 for (int i = 0; i < len; i++) { print(nums[i]); }
class Solution {
public int removeDuplicates(int[] nums) {
if (nums.length<3) return nums.length;
int pos = 1,flag = 1,last = nums[0];
for(int i = 1;i<nums.length;i++){
if (nums[i] == last) flag++;
else {
flag = 1;
last = nums[i];
}
if (flag <= 2) nums[pos++] = last;
}
return pos;
}
}
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,0,1,2,2,5,6]
可能变为 [2,5,6,0,0,1,2]
)。
编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true
,否则返回 false
。
示例 1:
输入: nums = [2,5,6,0,0,1,2]
, target = 0
输出: true
示例 2:
输入: nums = [2,5,6,0,0,1,2]
, target = 3
输出: false
进阶:
nums
可能包含重复元素。
class Solution { public boolean search(int[] nums, int target) { int start = 0, end = nums.length - 1, mid; while(start <= end) { mid = (start + end) / 2; if (nums[mid] == target) return true; if (nums[mid] < nums[end] || nums[mid] < nums[start]) { if (target > nums[mid] && target <= nums[end]) start = mid + 1; else end = mid - 1; } else if (nums[mid] > nums[start] || nums[mid] > nums[end]) { if (target < nums[mid] && target >= nums[start]) end = mid - 1; else start = mid + 1; } else end--; } return false; } }
给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。
示例 1:
输入: 1->2->3->3->4->4->5 输出: 1->2->5
示例 2:
输入: 1->1->1->2->3 输出: 2->3
class Solution { public ListNode deleteDuplicates(ListNode head) { if (head == null || head.next == null) { return head; } if (head.val == head.next.val) { if (head.next.next == null) { return null; } if (head.val == head.next.next.val) { return deleteDuplicates(head.next); } return deleteDuplicates(head.next.next); } head.next = deleteDuplicates(head.next); return head; } }
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 1:
输入: 1->1->2 输出: 1->2
示例 2:
输入: 1->1->2->3->3 输出: 1->2->3
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if (head == null || head.next == null) {
return head;
}
head.next = deleteDuplicates(head.next);
return head.val == head.next.val ? head.next : head;
}
}
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]
。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10
个单位。
示例:
输入: [2,1,5,6,2,3] 输出: 10
class Solution { public int largestRectangleArea(int[] heights) { if (heights == null || heights.length == 0) { return 0; } int n = heights.length; if (n == 1) { return heights[0]; } // 创建一个新的数组,数组长度为 n + 1,最后一个元素值赋为 0 // 确保在后面的遍历中,原数组最后一个元素值能得到计算 int[] heightss = new int[n + 1]; heightss[n] = 0; for (int i = 0; i < n; ++i) { heightss[i] = heights[i]; } Stack<Integer> stack = new Stack<>(); int max = 0; for (int i = 0; i <= n;) { if (stack.isEmpty() || heightss[i] > heightss[stack.peek()]) { stack.push(i++); } else { int index = stack.pop(); max = Math.max(max, heightss[index] * (stack.isEmpty() ? i : i - stack.peek() - 1)); } } return max; } }
给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
示例:
输入: [ ["1","0","1","0","0"], ["1","0","1","1","1"], ["1","1","1","1","1"], ["1","0","0","1","0"] ] 输出: 6
class Solution { public int maximalRectangle(char[][] matrix) { if(matrix==null || matrix.length==0) return 0; int result = 0; int[] row = new int[matrix[0].length]; for(char[] line : matrix){ update(line,row); result = Math.max(result, largestRectangleArea(row)); } return result; } private int largestRectangleArea(int[] heights) { int[] stack = new int[1 << 10]; int length = heights.length; int j, stackSize= 0, ma = 0, a; for (int i = 0; i <= length; i++) { while (stackSize > 0 &&( i==length || heights[i] < heights[stack[stackSize - 1]])) { j = stack[--stackSize]; a = (i - (stackSize == 0 ? 0 : stack[stackSize - 1] + 1)) * (heights[j]); if (a > ma) ma = a; } stack[stackSize++] = i; } return ma; } private void update(char[] line, int[] row){ for (int i = 0; i < row.length; i++) { if (line[i] == '0') row[i] = 0; else row[i]++; } } }
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
示例:
输入: head = 1->4->3->2->5->2, x = 3 输出: 1->2->2->4->3->5
class Solution { public ListNode partition(ListNode head, int x) { ListNode leftDummy = new ListNode(-1); ListNode rightDummy = new ListNode(-1); ListNode leftCur = leftDummy; ListNode rightCur = rightDummy; while (head != null) { if (head.val < x) { leftCur.next = head; leftCur = leftCur.next; } else { rightCur.next = head; rightCur = rightCur.next; } head = head.next; } leftCur.next = rightDummy.next; rightCur.next = null; return leftDummy.next; } }
给定一个字符串 s1,我们可以把它递归地分割成两个非空子字符串,从而将其表示为二叉树。
下图是字符串 s1 = "great"
的一种可能的表示形式。
great / \ gr eat / \ / \ g r e at / \ a t
在扰乱这个字符串的过程中,我们可以挑选任何一个非叶节点,然后交换它的两个子节点。
例如,如果我们挑选非叶节点 "gr"
,交换它的两个子节点,将会产生扰乱字符串 "rgeat"
。
rgeat / \ rg eat / \ / \ r g e at / \ a t
我们将 "rgeat”
称作 "great"
的一个扰乱字符串。
同样地,如果我们继续交换节点 "eat"
和 "at"
的子节点,将会产生另一个新的扰乱字符串 "rgtae"
。
rgtae / \ rg tae / \ / \ r g ta e / \ t a
我们将 "rgtae”
称作 "great"
的一个扰乱字符串。
给出两个长度相等的字符串 s1 和 s2,判断 s2 是否是 s1 的扰乱字符串。
示例 1:
输入: s1 = "great", s2 = "rgeat" 输出: true
示例 2:
输入: s1 = "abcde", s2 = "caebd" 输出: false
class Solution { public boolean isScramble(String s1, String s2) { if(s1.equals(s2)) return true; if(s1.length()!=s2.length()) return false; int len = s1.length(); int[] count = new int[26]; for(int i = 0; i < len; i++){ count[s1.charAt(i) - 'a']++; count[s2.charAt(i) - 'a']--; } for(int item : count) if (item != 0) return false; for(int i = 1; i <= len - 1; i++){ if(isScramble(s1.substring(0, i), s2.substring(0, i)) && isScramble(s1.substring(i), s2.substring(i))) return true; if (isScramble(s1.substring(0, i), s2.substring(len - i)) && isScramble(s1.substring(i), s2.substring(0, len - i))) return true; } return false; } }
给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 num1 成为一个有序数组。
说明:
示例:
输入: nums1 = [1,2,3,0,0,0], m = 3 nums2 = [2,5,6], n = 3 输出: [1,2,2,3,5,6]
双指针解决。
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
i, j = m - 1, n - 1
k = m + n - 1
while j >= 0:
if i >= 0 and nums1[i] > nums2[j]:
nums1[k] = nums1[i]
i -= 1
else:
nums1[k] = nums2[j]
j -= 1
k -= 1
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int i = m - 1, j = n - 1;
int k = m + n - 1;
while (j >= 0) {
if (i >= 0 && nums1[i] >= nums2[j]) {
nums1[k--] = nums1[i--];
} else {
nums1[k--] = nums2[j--];
}
}
}
}
格雷编码是一个二进制数字系统,在该系统中,两个连续的数值仅有一个位数的差异。
给定一个代表编码总位数的非负整数 n,打印其格雷编码序列。格雷编码序列必须以 0 开头。
示例 1:
输入: 2 输出:[0,1,3,2]
解释: 00 - 0 01 - 1 11 - 3 10 - 2 对于给定的 n,其格雷编码序列并不唯一。 例如,[0,2,3,1]
也是一个有效的格雷编码序列。 00 - 0 10 - 2 11 - 3 01 - 1
示例 2:
输入: 0 输出:[0] 解释: 我们定义
格雷编码序列必须以 0 开头。给定
编码总位数为n 的格雷编码序列,其长度为 2n
。当 n = 0 时,长度为 20 = 1。 因此,当 n = 0 时,其格雷编码序列为 [0]。
class Solution {
public List<Integer> grayCode(int n) {
List<Integer> re = new ArrayList<>();
for (int i = 0; i < (1 << n); i++) re.add(i ^ (i >> 1));
return re;
}
}
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: [1,2,2] 输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]
class Solution { public List<List<Integer>> subsetsWithDup(int[] nums) { List<List<Integer>> res = new ArrayList<>(); if(nums==null||nums.length==0) return res; Arrays.sort(nums); backTrack(res, nums, 0, new ArrayList<>()); return res; } private void backTrack(List<List<Integer>> res, int[] nums, int index, List<Integer> ls){ res.add(new ArrayList<>(ls)); if(index>=nums.length) return; for(int i=index;i<nums.length;i++){ ls.add(nums[i]); backTrack(res, nums, i+1, ls); ls.remove(ls.size()-1); while(i<nums.length-1 && nums[i+1]==nums[i]) i++; } } }
一条包含字母 A-Z
的消息通过以下方式进行了编码:
'A' -> 1 'B' -> 2 ... 'Z' -> 26
给定一个只包含数字的非空字符串,请计算解码方法的总数。
示例 1:
输入: "12" 输出: 2 解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。
示例 2:
输入: "226" 输出: 3 解释: 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
class Solution { public int numDecodings(String s) { int len = s.length(); if (len == 0) return 0; int current = s.charAt(0) == '0' ? 0 : 1; int last = 1; for (int i = 1; i < len; i++) { int tmp = current; if(s.charAt(i) == '0'){ if(s.charAt(i-1) == '1' || s.charAt(i-1) == '2') current = last; else return 0; }else if(s.charAt(i-1) == '1' || s.charAt(i-1) == '2' && s.charAt(i) <= '6') { current += last; } last = tmp; } return current; } }
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4 输出: 1->4->3->2->5->NULL
# Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode: if head is None or head.next is None or m == n: return head dummy = ListNode(0) dummy.next = head pre, cur = dummy, head for _ in range(m - 1): pre = cur cur = cur.next p, q = pre, cur for _ in range(n - m + 1): t = cur.next cur.next = pre pre = cur cur = t p.next = pre q.next = cur return dummy.next
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode reverseBetween(ListNode head, int m, int n) { if (head == null || head.next == null || m == n) { return head; } ListNode dummy = new ListNode(0); dummy.next = head; ListNode pre = dummy, cur = head; for (int i = 0; i < m - 1; ++i) { pre = cur; cur = cur.next; } ListNode p = pre, q = cur; for (int i = 0; i < n - m + 1; ++i) { ListNode t = cur.next; cur.next = pre; pre = cur; cur = t; } p.next = pre; q.next = cur; return dummy.next; } }
给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
示例:
输入: "25525511135"
输出: ["255.255.11.135", "255.255.111.35"]
class Solution { private List<String> result; private int length; public List<String> restoreIpAddresses(String s) { result = new ArrayList<>(); length = s.length(); int[] ip = new int[4]; restoreIpAddresses(s,0,ip,0); return result; } private void restoreIpAddresses(String s, int si, int[] ip, int pi) { int sl = length - si , pl = 3 - pi , i = -1; String pfx = null; while (si< length){ int num = s.charAt(si++) - '1' + 1; if (i==0) break; i = i == -1 ? num : i * 10 + num; sl--; if (i>255) break; if (sl < pl || sl > pl * 3) continue; if (pi==3){ if (pfx==null){ StringBuilder pfxBuilder = new StringBuilder(); for (int j = 0; j < ip.length-1; j++) pfxBuilder.append(ip[j]).append('.'); pfx = pfxBuilder.toString(); } result.add(pfx + i); } ip[pi] = i; restoreIpAddresses(s,si,ip,pi+1); } } }
给定一个二叉树,返回它的中序 遍历。
示例:
输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
// 递归版本 /* class Solution { public List<Integer> inorderTraversal(TreeNode root) { List<Integer> list = new ArrayList<>(); inorderTraversal(root, list); return list; } private void inorderTraversal(TreeNode root, List<Integer> list) { if (root == null) { return; } inorderTraversal(root.left, list); list.add(root.val); inorderTraversal(root.right, list); } } */ // 非递归版本 class Solution { public List<Integer> inorderTraversal(TreeNode root) { List<Integer> list = new ArrayList<>(); Stack<TreeNode> stack = new Stack<>(); while (root != null || !stack.isEmpty()) { while (root != null) { stack.push(root); root = root.left; } if (!stack.isEmpty()) { root = stack.pop(); list.add(root.val); root = root.right; } } return list; } }
给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树。
示例:
输入: 3 输出: [ [1,null,3,2], [3,2,null,1], [3,1,null,null,2], [2,1,3], [1,null,2,null,3] ] 解释: 以上的输出对应以下 5 种不同结构的二叉搜索树: 1 3 3 2 1 \ / / / \ \ 3 2 1 1 3 2 / / \ \ 2 1 2 3
class Solution { public List<TreeNode> generateTrees(int n) { if (n <= 0) return new ArrayList<>(); return generateTrees(1, n); } private List<TreeNode> generateTrees(int left, int right) { List<TreeNode> list = new ArrayList<>(); if (left > right) { list.add(null); } else { for (int i = left; i <= right; i++) { List<TreeNode> leftTrees = generateTrees(left, i - 1); List<TreeNode> rightTrees = generateTrees(i + 1, right); for (TreeNode l : leftTrees) { for (TreeNode r : rightTrees) { TreeNode root = new TreeNode(i); root.left = l; root.right = r; list.add(root); } } } } return list; } }
给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?
示例:
输入: 3 输出: 5 解释: 给定 n = 3, 一共有 5 种不同结构的二叉搜索树: 1 3 3 2 1 \ / / / \ \ 3 2 1 1 3 2 / / \ \ 2 1 2 3
class Solution {
public int numTrees(int n) {
// res[n] 表示整数n组成的二叉搜索树个数
int[] res = new int[n + 1];
res[0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < i; ++j) {
res[i] += res[j] * res[i - j - 1];
}
}
return res[n];
}
}
给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的。
示例 1:
输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac" 输出: true
示例 2:
输入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc" 输出: false
public class Solution { public boolean isInterleave(String s1, String s2, String s3) { char[] c1 = s1.toCharArray(), c2 = s2.toCharArray(), c3 = s3.toCharArray(); int m = s1.length(), n = s2.length(); if(m + n != s3.length()) return false; return dfs(c1, c2, c3, 0, 0, 0, new boolean[m + 1][n + 1]); } private boolean dfs(char[] c1, char[] c2, char[] c3, int i, int j, int k, boolean[][] invalid) { if(invalid[i][j]) return false; if(k == c3.length) return true; boolean valid = i < c1.length && c1[i] == c3[k] && dfs(c1, c2, c3, i + 1, j, k + 1, invalid) || j < c2.length && c2[j] == c3[k] && dfs(c1, c2, c3, i, j + 1, k + 1, invalid); if(!valid) invalid[i][j] = true; return valid; } }
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
示例 1:
输入: 2 / \ 1 3 输出: true
示例 2:
输入: 5 / \ 1 4 / \ 3 6 输出: false 解释: 输入为: [5,1,4,null,null,3,6]。 根节点的值为 5 ,但是其右子节点值为 4 。
class Solution {
private long current = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
if (isValidBST(root.left) && current < root.val) {
current = root.val;
return isValidBST(root.right);
}
return false;
}
}
二叉搜索树中的两个节点被错误地交换。
请在不改变其结构的情况下,恢复这棵树。
示例 1:
输入: [1,3,null,null,2] 1 / 3 \ 2 输出: [3,1,null,null,2] 3 / 1 \ 2
示例 2:
输入: [3,1,4,null,null,2] 3 / \ 1 4 / 2 输出: [2,1,4,null,null,3] 2 / \ 1 4 / 3
进阶:
class Solution { private TreeNode first,second,pre; public void recoverTree(TreeNode root) { traverse(root); int temp = first.val; first.val = second.val; second.val = temp; } private void traverse(TreeNode root) { if (root == null) return; traverse(root.left); if (pre != null) { if (first == null && pre.val > root.val) first = pre; if (first != null && pre.val > root.val) second = root; } pre = root; traverse(root.right); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。