每个题的【方法1】是自己的思路,【其他方法】是力扣上更优的解题思路
目录
一、数组、字符串
1. 合并两个有序数组 ①
2. 移除元素 ①
3. 删除有序数组中的重复项 ①
4. 删除有序数组中的重复项 II ②
5. 多数元素 ①
6. 轮转数组 ②
7. 买卖股票的最佳时机 ①
8. 买卖股票的最佳时机 II ②
9. 跳跃游戏 ②
10. 跳跃游戏II ②
11. H指数 ②
12. O(1)时间插入、删除和获取随机元素 ②
13. 除自身以外数组的乘积 ②
14. 加油站 ②
15. 分发糖果 ③
16. 接雨水 ③
17. 罗马数字转整数 ①
18. 整数转罗马数字 ②
19. 最后一个单词的长度 ①
20. 最长公共前缀 ①
21. 反转字符串中的单词 ②
22. Z字形变换 ②
23. 找出字符串中第一个匹配项的下标 ①
24. 文本左右对齐 ③
一、数组、字符串
1. 合并两个有序数组 ①
给你两个按 非递减顺序 排列的整数数组 nums1
和 nums2
,另有两个整数 m
和 n
,分别表示 nums1
和 nums2
中的元素数目。
请你 合并 nums2
到 nums1
中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1
中。为了应对这种情况,nums1
的初始长度为 m + n
,其中前 m
个元素表示应合并的元素,后 n
个元素为 0
,应忽略。nums2
的长度为 n
。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3 输出:[1,2,2,3,5,6] 解释:需要合并 [1,2,3] 和 [2,5,6] 。 合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
示例 2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0 输出:[1] 解释:需要合并 [1] 和 [] 。 合并结果是 [1] 。
示例 3:
输入:nums1 = [0], m = 0, nums2 = [1], n = 1 输出:[1] 解释:需要合并的数组是 [] 和 [1] 。 合并结果是 [1] 。 注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。
提示:
nums1.length == m + n
nums2.length == n
0 <= m, n <= 200
1 <= m + n <= 200
-109 <= nums1[i], nums2[j] <= 109
方法1:数组合并后再调用内置函数进行排序(1ms)
public static void merge(int[] nums1, int m, int[] nums2, int n) {
for(int i = 0; i < n; i++){
nums1[m + i] = nums2[i];
}
Arrays.sort(nums1);
}
方法2:两个指针对两个数组中元素一一比较(0ms)
public static void merge(int[] nums1, int m, int[] nums2, int n) {
int i = m - 1;
int j = n - 1;
for (int k = m + n - 1; k >= 0; k--) {
if (i < 0){
nums1[k] = nums2[j];
j--;
continue;
}
if (j < 0){
nums1[k] = nums1[i];
i--;
continue;
}
if (nums1[i] > nums2[j]){
nums1[k] = nums1[i];
i--;
}else {
nums1[k] = nums2[j];
j--;
}
}
}
方法3:类似于方法2
public void merge(int[] nums1, int m, int[] nums2, int n) {
if(n==0) return;
int i = m-1,j=n-1,k = m+n-1;
while(i>=0&&j>=0){
if(nums1[i]>nums2[j]){
nums1[k--] = nums1[i--];
}else{
nums1[k--] = nums2[j--];
}
}
while(j>=0){
nums1[k--] = nums2[j--];
}
}
2. 移除元素 ①
给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1)
额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝 int len = removeElement(nums, val); // 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。 for (int i = 0; i < len; i++) { print(nums[i]); }
示例 1:
输入:nums = [3,2,2,3], val = 3 输出:2, nums = [2,2] 解释:函数应该返回新的长度 2,并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2 输出:5, nums = [0,1,3,0,4] 解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0,1,3,0,4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
方法1:
public int removeElement(int[] nums, int val) {
ArrayList<Integer> list = new ArrayList<>();
int cnt = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != val){
list.add(nums[i]);
}
}
for (Integer integer : list) {
nums[cnt++] = integer;
}
return list.size();
}
方法2:
public int removeElement(int[] nums, int val) {
int left = 0;
int right = 0;
while(right < nums.length) {
if(nums[right] == val) {
right++;
} else{
nums[left++] = nums[right++];
}
}
return left;
}
3. 删除有序数组中的重复项 ①
给你一个 非严格递增排列 的数组 nums
,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums
中唯一元素的个数。
考虑 nums
的唯一元素的数量为 k
,你需要做以下事情确保你的题解可以被通过:
- 更改数组
nums
,使nums
的前k
个元素包含唯一元素,并按照它们最初在nums
中出现的顺序排列。nums
的其余元素与nums
的大小不重要。 - 返回
k
。
判题标准:
系统会用下面的代码来测试你的题解:
int[] nums = [...]; // 输入数组 int[] expectedNums = [...]; // 长度正确的期望答案 int k = removeDuplicates(nums); // 调用 assert k == expectedNums.length; for (int i = 0; i < k; i++) { assert nums[i] == expectedNums[i]; }
如果所有断言都通过,那么您的题解将被 通过。
示例 1:
输入:nums = [1,1,2] 输出:2, nums = [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] 解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。不需要考虑数组中超出新长度后面的元素。
提示:
1 <= nums.length <= 3 * 104
-104 <= nums[i] <= 104
nums
已按 非严格递增 排列
方法1:
public static int removeDuplicates(int[] nums) {
int i = 1;
int cnt = 1;
while (i < nums.length){
if (nums[i] == nums[i - 1]){
i++;
continue;
}else {
nums[cnt++] = nums[i++];
}
}
return cnt;
}
方法2:
public int removeDuplicates1(int[] nums) {
int slow =0;
for(int fast =1; fast < nums.length; fast++){
if(nums[fast] != nums[slow]){
slow++;
nums[slow] = nums[fast];
}
}
return slow+1;
}
4. 删除有序数组中的重复项 II ②
给你一个有序数组 nums
,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 int len = removeDuplicates(nums); // 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。 for (int i = 0; i < len; i++) { print(nums[i]); }
示例 1:
输入:nums = [1,1,1,2,2,3] 输出:5, nums = [1,1,2,2,3] 解释:函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为1, 1, 2, 2, 3。 不需要考虑数组中超出新长度后面的元素。
示例 2:
输入:nums = [0,0,1,1,1,1,2,3,3] 输出:7, nums = [0,0,1,1,2,3,3] 解释:函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3。不需要考虑数组中超出新长度后面的元素。
提示:
1 <= nums.length <= 3 * 104
-104 <= nums[i] <= 104
nums
已按升序排列
方法1:
public static int removeDuplicates(int[] nums) {
int index = 0;
int count = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] == nums[index] && count == 1){
index++;
nums[index] = nums[i];
count++;
}
if (nums[i] != nums[index]){
index++;
nums[index] = nums[i];
count = 1;
}
}
return index + 1;
}
方法2:
public static int removeDuplicates1(int[] nums) {
if(nums.length<=2) return nums.length;
int slow = 2;
for(int fast = 2;fast<nums.length;fast++)
{
if(nums[fast]!=nums[slow-2])
{
nums[slow++] = nums[fast];
}
}
return slow;
}
方法3:
class Solution {
public int removeDuplicates(int[] nums) {
return process(nums, 2);
}
int process(int[] nums, int k) {
int u = 0;
for (int x : nums) {
if (u < k || nums[u - k] != x) nums[u++] = x;
}
return u;
}
}
作者:宫水三叶
链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii/
来源:力扣(LeetCode)
5. 多数元素 ①
给定一个大小为 n
的数组 nums
,返回其中的多数元素。多数元素是指在数组中出现次数 大于
⌊ n/2 ⌋
的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:nums = [3,2,3] 输出:3
示例 2:
输入:nums = [2,2,1,1,1,2,2] 输出:2
提示:
n == nums.length
1 <= n <= 5 * 104
-109 <= nums[i] <= 109
方法一:
public static int majorityElement(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
int length = nums.length / 2;
Integer result = 0;
Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
for (Map.Entry<Integer, Integer> entry : entries) {
if (entry.getValue() > length){
result = entry.getKey();
}
}
return result;
}
方法2: 0秒
private int quickSort(int[] nums, int l, int r, int k) {
if (l >= r) return nums[l];
int x = nums[l + r >> 1];
int i = l - 1;
int j = r + 1;
while (i < j) {
while (nums[++i] < x) ;
while (nums[--j] > x) ;
if (i < j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
return k <= j ? quickSort(nums, l, j, k) : quickSort(nums, j + 1, r, k);
}
public int majorityElement(int[] nums) {
return quickSort(nums, 0, nums.length - 1, nums.length / 2);
}
方法3:1秒
public int majorityElement(int[] nums) {
int res = nums[0];
int count = 1;
for(int i = 1; i < nums.length; i++){
if(nums[i] == res){
count++;
}
else if(count > 1){
count--;
}
else{
res = nums[i];
}
}
return res;
}
6. 轮转数组 ②
给定一个整数数组 nums
,将数组中的元素向右轮转 k
个位置,其中 k
是非负数。
示例 1:
输入: nums = [1,2,3,4,5,6,7], k = 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7,1,2,3,4,5] 向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入:nums = [-1,-100,3,99], k = 2 输出:[3,99,-1,-100] 解释: 向右轮转 1 步: [99,-1,-100,3] 向右轮转 2 步: [3,99,-1,-100]
提示:
1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
方法1:
public static void rotate(int[] nums, int k) {
int length = nums.length;
int[] newArr = new int[length];
k = k > length? k % length : k;
for (int i = 0; i < nums.length; i++) {
newArr[i] = nums[(length + i - k) % length];
}
System.arraycopy(newArr, 0, nums, 0, length);
}
方法2:
public void rotate(int[] nums, int k) {
//翻转数组
k = k % nums.length;
reverse(nums,0,nums.length-1);
reverse(nums,0,k-1);
reverse(nums,k,nums.length-1);
}
public void reverse(int[] nums, int start,int end){
while(start<end){
int temp = nums[start];
nums[start++] = nums[end];
nums[end--] = temp;
}
}
7. 买卖股票的最佳时机 ①
给定一个数组 prices
,它的第 i
个元素 prices[i]
表示一支给定股票第 i
天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
。
示例 1:
输入:[7,1,5,3,6,4] 输出:5 解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入:prices = [7,6,4,3,1] 输出:0 解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
提示:
1 <= prices.length <= 105
0 <= prices[i] <= 104
方法一:
public static int maxProfit(int[] prices) {
int max = 0;
int min = Integer.MAX_VALUE;
for (int i = 0; i < prices.length; i++) {
if (prices[i] < min){
min = prices[i];
}else {
max = prices[i] - min > max? prices[i] - min:max;
}
}
return max;
}
方法二:
public int maxProfit(int[] prices) {
int cost = Integer.MAX_VALUE, profit = 0;
for (int price : prices) {
cost = Math.min(cost, price);
profit = Math.max(profit, price - cost);
}
return profit;
}
作者:Krahets
链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/
8. 买卖股票的最佳时机 II ②
给你一个整数数组 prices
,其中 prices[i]
表示某支股票第 i
天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
示例 1:
输入:prices = [7,1,5,3,6,4] 输出:7 解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。 总利润为 4 + 3 = 7 。
示例 2:
输入:prices = [1,2,3,4,5] 输出:4 解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。 总利润为 4 。
示例 3:
输入:prices = [7,6,4,3,1] 输出:0 解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。
提示:
1 <= prices.length <= 3 * 104
0 <= prices[i] <= 104
方法1:
public static int maxProfit(int[] prices) {
int max = 0;
int left = 0;
for (int i = 1; i < prices.length; i++) {
if (prices[i] > prices[i - 1]){
left++;
if (i == prices.length - 1){
max += prices[i] - prices[i - left];
}
}else {
max += prices[i - 1] - prices[i - 1 - left];
left = 0;
}
}
return max;
}
方法2:
public int maxProfit(int[] prices) {
int profit = 0;
for (int i = 1; i < prices.length; i++) {
int tmp = prices[i] - prices[i - 1];
if (tmp > 0) profit += tmp;
}
return profit;
}
作者:Krahets
链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/
9. 跳跃游戏 ②
给你一个非负整数数组 nums
,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标,如果可以,返回 true
;否则,返回 false
。
示例 1:
输入:nums = [2,3,1,1,4] 输出:true 解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:
输入:nums = [3,2,1,0,4] 输出:false 解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
提示:
1 <= nums.length <= 104
0 <= nums[i] <= 105
方法二:
public static boolean canJump(int[] nums) {
if (nums == null) {
return false;
}
//前n-1个元素能够跳到的最远距离
int k = 0;
for (int i = 0; i <= k; i++) {
//第i个元素能够跳到的最远距离
int temp = i + nums[i];
//更新最远距离
k = Math.max(k, temp);
//如果最远距离已经大于或等于最后一个元素的下标,则说明能跳过去,退出. 减少循环
if (k >= nums.length - 1) {
return true;
}
}
//最远距离k不再改变,且没有到末尾元素
return false;
}
10. 跳跃游戏II ②
给定一个长度为 n
的 0 索引整数数组 nums
。初始位置为 nums[0]
。
每个元素 nums[i]
表示从索引 i
向前跳转的最大长度。换句话说,如果你在 nums[i]
处,你可以跳转到任意 nums[i + j]
处:
0 <= j <= nums[i]
i + j < n
返回到达 nums[n - 1]
的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]
。
示例 1:
输入: nums = [2,3,1,1,4] 输出: 2 解释: 跳到最后一个位置的最小跳跃数是 2。 从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:
输入: nums = [2,3,0,1,4] 输出: 2
提示:
1 <= nums.length <= 104
0 <= nums[i] <= 1000
- 题目保证可以到达
nums[n-1]
方法二:
public int jump(int[] nums) {
if (nums == null || nums.length <= 1) return 0;
int currJumpMax = 0, //以当前跳跃步数,能到的最远位置,比如: jump=1跳一次时,最远能到下标currJumpMax=2
currPostMax = 0, //当前位置能到的最远位置
jump = 0,
i = 0;
while (i < nums.length - 1) { //不需要检查最后一个位置是因为,最后一个位置我们不用跳了已经
currPostMax = Math.max(currPostMax, i + nums[i]);
if (i == currJumpMax) { //已经走到了当前跳跃步数的边界,
jump++; //我们不得不再跳一次
currJumpMax = currPostMax; //并记录当前跳跃步数能到的最远位置
}
i++;
}
return jump;
}
11. H指数 ②
给你一个整数数组 citations
,其中 citations[i]
表示研究者的第 i
篇论文被引用的次数。计算并返回该研究者的 h
指数。
根据维基百科上 h 指数的定义:h
代表“高引用次数” ,一名科研人员的 h
指数 是指他(她)至少发表了 h
篇论文,并且 至少 有 h
篇论文被引用次数大于等于 h
。如果 h
有多种可能的值,h
指数 是其中最大的那个。
示例 1:
输入:citations = [3,0,6,1,5] 输出:3 解释:给定数组表示研究者总共有 5 篇论文,每篇论文相应的被引用了 3, 0, 6, 1, 5 次。 由于研究者有 3 篇论文每篇 至少 被引用了 3 次,其余两篇论文每篇被引用 不多于 3 次,所以她的 h 指数是 3。
示例 2:
输入:citations = [1,3,1] 输出:1
提示:
n == citations.length
1 <= n <= 5000
0 <= citations[i] <= 1000
方法2:
public int hIndex(int[] cs) {
int n = cs.length;
int l = 0, r = n;
while (l < r) {
int mid = l + r + 1 >> 1;
if (check(cs, mid)) l = mid;
else r = mid - 1;
}
return r;
}
boolean check(int[] cs, int mid) {
int ans = 0;
for (int i : cs) if (i >= mid) ans++;
return ans >= mid;
}
作者:宫水三叶
链接:https://leetcode.cn/problems/h-index/
方法3:
public int hIndex(int[] citations) {
Arrays.sort(citations);
int hIndex = 0;
int n = citations.length-1;
for (int i = n; i >= 0; i--) {
if(citations[i] > hIndex ) {
hIndex++;
}
}
return citations[n] == 0?0:hIndex;
}
12. O(1)时间插入、删除和获取随机元素 ②
13. 除自身以外数组的乘积 ②
给你一个整数数组 nums
,返回 数组 answer
,其中 answer[i]
等于 nums
中除 nums[i]
之外其余各元素的乘积 。
题目数据 保证 数组 nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请 不要使用除法,且在 O(n)
时间复杂度内完成此题。
示例 1:
输入: nums = [1,2,3,4] 输出: [24,12,8,6]
示例 2:
输入: nums = [-1,1,0,-3,3] 输出: [0,0,9,0,0]
提示:
2 <= nums.length <= 105
-30 <= nums[i] <= 30
- 保证 数组
nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内
进阶:你可以在 O(1)
的额外空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组 不被视为 额外空间。)
方法1:(超出时间限制,通过18/22)
public static int[] productExceptSelf(int[] nums) {
int[] answer = new int[nums.length];
Arrays.fill(answer, 1);
for (int i = 0; i < nums.length; i++) {
int index = 0;
while (index <nums.length){
if (index != i){
answer[i] *= nums[index];
}
index++;
}
}
return answer;
}
方法2:(0ms)
public int[] productExceptSelf(int[] nums) {
int[] result = new int[nums.length];
int initial = 1;
for(int i = 0; i < nums.length; i++){
result[i] = initial;
initial *= nums[i];
}
initial = 1;
for(int j = nums.length - 1; j >= 0; j--){
result[j] *= initial;
initial *= nums[j];
}
return result;
}
14. 加油站 ②
在一条环路上有 n
个加油站,其中第 i
个加油站有汽油 gas[i]
升。
你有一辆油箱容量无限的的汽车,从第 i
个加油站开往第 i+1
个加油站需要消耗汽油 cost[i]
升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas
和 cost
,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1
。如果存在解,则 保证 它是 唯一 的。
示例 1:
输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2] 输出: 3 解释: 从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油 开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油 开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油 开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油 开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油 开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。 因此,3 可为起始索引。
示例 2:
输入: gas = [2,3,4], cost = [3,4,3] 输出: -1 解释: 你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。 我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油 开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油 开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油 你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。 因此,无论怎样,你都不可能绕环路行驶一周。
提示:
gas.length == n
cost.length == n
1 <= n <= 105
0 <= gas[i], cost[i] <= 104
方法1:(34/40通过)
public static int canCompleteCircuit(int[] gas, int[] cost) {
for (int i = 0; i < gas.length; i++) {
if (gas[i] < cost[i]){
continue;
}else {
int index = 0;
int res = gas[i];
int result = cost[(i - 1 + gas.length) % gas.length];
while (index < gas.length - 1){
res -= cost[(i + index) % gas.length];
if (res < 0){
break;
}else {
res = res + gas[(i + 1 + index) % gas.length];
index++;
}
}
if (res == result){
return i;
}
}
}
return -1;
}
方法2:(0ms)
public int canCompleteCircuit(int[] gas, int[] cost) {
int res = 0;
int tank = 0;
int need = 0;
for (int i = 0; i < gas.length; i++) {
tank += (gas[i] - cost[i]);
if (tank < 0) {
res = i + 1;
need += tank;
tank = 0;
}
}
if (tank + need >= 0) {
return res;
}
return -1;
}
方法3:(1ms)
public int canCompleteCircuit(int[] gas, int[] cost) {
int n = gas.length;
int sum = 0;
int minSum = Integer.MAX_VALUE, minSumIndex = 0;
for (int i = 0; i < n; i++){
sum += gas[i] - cost[i];
if (sum < minSum){
minSumIndex = i;
minSum = sum;
}
}
if (sum < 0) return -1;
else if (minSum >= 0) return 0;
else return (minSumIndex + 1) % n;
}
15. 分发糖果 ③
16. 接雨水 ③
17. 罗马数字转整数 ①
罗马数字包含以下七种字符: 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:
输入: s = "III" 输出: 3
示例 2:
输入: s = "IV" 输出: 4
示例 3:
输入: s = "IX" 输出: 9
示例 4:
输入: s = "LVIII" 输出: 58 解释: L = 50, V= 5, III = 3.
示例 5:
输入: s = "MCMXCIV" 输出: 1994 解释: M = 1000, CM = 900, XC = 90, IV = 4.
提示:
1 <= s.length <= 15
s
仅含字符('I', 'V', 'X', 'L', 'C', 'D', 'M')
- 题目数据保证
s
是一个有效的罗马数字,且表示整数在范围[1, 3999]
内 - 题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
- IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
- 关于罗马数字的详尽书写规则,可以参考 罗马数字 - Mathematics 。
方法一:
public int romanToInt(String s) {
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
int num = charToInt(c);
if (stack.size() == 0 || num <= stack.peek()) {
stack.push(num);
}else {
Integer pop = stack.pop();
stack.push(num - pop);
}
}
int sum = 0;
while (stack.size() > 0){
sum += stack.pop();
}
return sum;
}
public static int charToInt(char c){
int symbol = 0;
switch (c){
case 'I':
symbol = 1;
break;
case 'V':
symbol = 5;
break;
case 'X':
symbol = 10;
break;
case 'L':
symbol = 50;
break;
case 'C':
symbol = 100;
break;
case 'D':
symbol = 500;
break;
case 'M':
symbol = 1000;
break;
}
return symbol;
}
方法2:
public int romanToInt(String s) {
int sum = 0;
int preNum = getValue(s.charAt(0));
for(int i = 1;i < s.length(); i ++) {
int num = getValue(s.charAt(i));
if(preNum < num) {
sum -= preNum;
} else {
sum += preNum;
}
preNum = num;
}
sum += preNum;
return sum;
}
private int getValue(char ch) {
switch(ch) {
case 'I': return 1;
case 'V': return 5;
case 'X': return 10;
case 'L': return 50;
case 'C': return 100;
case 'D': return 500;
case 'M': return 1000;
default: return 0;
}
}
18. 整数转罗马数字 ②
罗马数字包含以下七种字符: 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:
输入: num = 3 输出: "III"
示例 2:
输入: num = 4 输出: "IV"
示例 3:
输入: num = 9 输出: "IX"
示例 4:
输入: num = 58 输出: "LVIII" 解释: L = 50, V = 5, III = 3.
示例 5:
输入: num = 1994 输出: "MCMXCIV" 解释: M = 1000, CM = 900, XC = 90, IV = 4.
提示:
1 <= num <= 3999
方法1:
public static String intToRoman(int num) {
int[] nums = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
StringBuilder stringBuilder = new StringBuilder();
while (num > 0){
for (int i = 0; i < nums.length; ) {
while (num >= nums[i]){
stringBuilder.append(toString(nums[i]));
num -= nums[i];
}
i++;
}
}
return stringBuilder.toString();
}
public static String toString(int num){
switch (num){
case 1000:
return "M";
case 900:
return "CM";
case 500:
return "D";
case 400:
return "CD";
case 100:
return "C";
case 90:
return "XC";
case 50:
return "L";
case 40:
return "XL";
case 10:
return "X";
case 9:
return "IX";
case 5:
return "V";
case 4:
return "IV";
case 1:
return "I";
}
return "";
}
方法2:
public String intToRoman(int num) {
StringBuilder sb = new StringBuilder();
int remains = num;
while (remains > 0) {
if (remains >= 1000) {
sb.append('M');
remains -= 1000;
} else if (remains >= 100) {
if (remains >= 900) {
sb.append("CM");
remains -= 900;
} else if (remains >= 500) {
sb.append('D');
remains -= 500;
} else if (remains >= 400) {
sb.append("CD");
remains -= 400;
} else {
sb.append('C');
remains -= 100;
}
} else if (remains >= 10) {
if (remains >= 90) {
sb.append("XC");
remains -= 90;
} else if (remains >= 50) {
sb.append('L');
remains -= 50;
} else if (remains >= 40) {
sb.append("XL");
remains -= 40;
} else {
sb.append('X');
remains -= 10;
}
} else {
if (remains >= 9) {
sb.append("IX");
remains -= 9;
} else if (remains >= 5) {
sb.append('V');
remains -= 5;
} else if (remains >= 4) {
sb.append("IV");
remains -= 4;
} else {
sb.append('I');
remains -= 1;
}
}
}
return sb.toString();
}
方法3:
int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
String[] symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
public String intToRoman(int num) {
StringBuffer roman = new StringBuffer();
for (int i = 0; i < values.length; ++i) {
int value = values[i];
String symbol = symbols[i];
while (num >= value) {
num -= value;
roman.append(symbol);
}
if (num == 0) {
break;
}
}
return roman.toString();
}
19. 最后一个单词的长度 ①
给你一个字符串 s
,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。
示例 1:
输入:s = "Hello World" 输出:5 解释:最后一个单词是“World”,长度为5。
示例 2:
输入:s = " fly me to the moon " 输出:4 解释:最后一个单词是“moon”,长度为4。
示例 3:
输入:s = "luffy is still joyboy" 输出:6 解释:最后一个单词是长度为6的“joyboy”。
提示:
1 <= s.length <= 104
s
仅有英文字母和空格' '
组成s
中至少存在一个单词
方法1:(0ms)
public static int lengthOfLastWord(String s) {
int right = s.length() - 1;
while (s.charAt(right) == ' '){
right--;
}
int index = right;
while (index >= 0 && s.charAt(index) != ' '){
index--;
}
return right - index;
}
20. 最长公共前缀 ①
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""
。
示例 1:
输入:strs = ["flower","flow","flight"] 输出:"fl"
示例 2:
输入:strs = ["dog","racecar","car"] 输出:"" 解释:输入不存在公共前缀。
提示:
1 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i]
仅由小写英文字母组成
方法1:(1ms)
public static String longestCommonPrefix(String[] strs) {
if (strs.length == 1){
return strs[0];
}
int index = 0;
String str = strs[0];
while (index < str.length()) {
char c = str.charAt(index);
for (int i = 1; i < strs.length; i++) {
if (strs[i].length() <= index){
return str.substring(0, index);
}
char c1 = strs[i].charAt(index);
if (c1 != c){
return str.substring(0, index);
}
}
index++;
}
return str.substring(0, index);
}
方法2:(0ms)
public String longestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0) {
return "";
}
String prefix = strs[0];
int count = strs.length;
for (int i = 1; i < count; i++) {
prefix = longestCommonPrefix(prefix, strs[i]);
if (prefix.length() == 0) {
break;
}
}
return prefix;
}
public String longestCommonPrefix(String str1, String str2) {
int length = Math.min(str1.length(), str2.length());
int index = 0;
while (index < length && str1.charAt(index) == str2.charAt(index)) {
index++;
}
return str1.substring(0, index);
}
21. 反转字符串中的单词 ②
给你一个字符串 s
,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s
中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s
中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
示例 1:
输入:s = "the sky is blue
" 输出:"blue is sky the
"
示例 2:
输入:s = " hello world " 输出:"world hello" 解释:反转后的字符串中不能存在前导空格和尾随空格。
示例 3:
输入:s = "a good example" 输出:"example good a" 解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。
提示:
1 <= s.length <= 104
s
包含英文大小写字母、数字和空格' '
s
中 至少存在一个 单词
进阶:如果字符串在你使用的编程语言中是一种可变数据类型,请尝试使用 O(1)
额外空间复杂度的 原地 解法。
方法1:(6ms)
public static String reverseWords(String s) {
Stack<String> stack = new Stack<>();
StringBuilder stringBuilder = new StringBuilder();
String[] s1 = s.split(" ");
for (String s2 : s1) {
if (!"".equals(s2)){
stack.push(s2);
}
}
while (stack.size() > 0){
stringBuilder.append(stack.pop()).append(" ");
}
return stringBuilder.toString().substring(0, stringBuilder.length() - 1);
}
方法2:
22. Z字形变换 ②
将一个给定字符串 s
根据给定的行数 numRows
,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING"
行数为 3
时,排列如下:
P A H N A P L S I I G Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"
。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
示例 1:
输入:s = "PAYPALISHIRING", numRows = 3 输出:"PAHNAPLSIIGYIR"
示例 2:
输入:s = "PAYPALISHIRING", numRows = 4 输出:"PINALSIGYAHRPI" 解释: P I N A L S I G Y A H R P I
示例 3:
输入:s = "A", numRows = 1 输出:"A"
提示:
1 <= s.length <= 1000
s
由英文字母(小写和大写)、','
和'.'
组成1 <= numRows <= 1000
方法1:
public static String convert(String s, int numRows) {
if (numRows == 1){
return s;
}
StringBuilder[] stringBuilders = new StringBuilder[numRows];
for (int i = 0; i < numRows; i++) {
stringBuilders[i] = new StringBuilder();
}
boolean flag = true;
int index = 0;
int[] indexArr = new int[s.length()];
for (int i = 0; i < s.length(); i++) {
indexArr[i] = index;
if (index == 0){
flag = true;
}
if (index == numRows - 1){
flag = false;
}
if (flag){
index++;
}else {
index--;
}
}
for (int i = 0; i < s.length(); i++) {
stringBuilders[indexArr[i]].append(s.charAt(i));
}
for (int i = 1; i < numRows; i++) {
stringBuilders[0].append(stringBuilders[i]);
}
return stringBuilders[0].toString();
}
23. 找出字符串中第一个匹配项的下标 ①
给你两个字符串 haystack
和 needle
,请你在 haystack
字符串中找出 needle
字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle
不是 haystack
的一部分,则返回 -1
。
示例 1:
输入:haystack = "sadbutsad", needle = "sad" 输出:0 解释:"sad" 在下标 0 和 6 处匹配。 第一个匹配项的下标是 0 ,所以返回 0 。
示例 2:
输入:haystack = "leetcode", needle = "leeto" 输出:-1 解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1 。
提示:
1 <= haystack.length, needle.length <= 104
haystack
和needle
仅由小写英文字符组成
方法1:(0ms)
public static int strStr(String haystack, String needle) {
for (int i = 0; i < haystack.length() - needle.length() + 1; i++) {
String substring = haystack.substring(i, i + needle.length());
if (substring.equals(needle)){
return i;
}
}
return -1;
}