赞
踩
给你两个按 非递减顺序 排列的整数数组 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
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) { int p1 = 0, p2 = 0;//双指针 int s[m + n];//建立新的数组用于保存 int temp; while (p1 < m || p2 < n) { if (p1 == m) {//先判断边界,否则会报错 temp = nums2[p2]; p2++; } else if (p2 == n) { temp = nums1[p1]; p1++; } else if (nums1[p1] < nums2[p2]) { temp = nums1[p1]; p1++; } else { temp = nums2[p2]; p2++; } s[p1 + p2 - 1] = temp;//找规律,别用索引 } for (int i = 0; i != m + n; ++i) { nums1[i] = s[i]; } }
class Solution {//c++
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
for (int i = 0; i != n; ++i) {
nums1[m + i] = nums2[i];
}
sort(nums1.begin(), nums1.end());
}
};
class Solution {//java
public 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);
}
}
给你一个数组 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,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
法一: int removeElement(int* nums, int numsSize, int val) { int left = 0; for (int right = 0; right < numsSize; right++) { if (nums[right] != val) { nums[left] = nums[right]; left++; } } return left; } 法二:(契合题干) int removeElement(int* nums, int numsSize, int val){//双指针,左侧指针如果与待删除的值相同,则传右侧指针的值 int a = 0, b = numsSize; while(a < b) { if(nums[a] == val) { nums[a] = nums[b - 1]; b--; } else//如果不同,就不用删除,接着遍历 { a++; } } return a; }
class Solution {//c++
public:
int removeElement(vector<int>& nums, int val) {
int n = nums.size();
int left = 0;
for (int right = 0; right < n; right++) {
if (nums[right] != val) {
nums[left] = nums[right];
left++;
}
}
return left;
}
};
class Solution {//java
public int removeElement(int[] nums, int val) {
int left = 0;
int right = nums.length;
while (left < right) {
if (nums[left] == val) {
nums[left] = nums[right - 1];
right--;
} else {
left++;
}
}
return left;
}
}
给你一个 升序排列 的数组 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
已按 升序 排列int removeDuplicates(int* nums, int numsSize){
int left = 0, right = 1;//双指针遍历
while(right < numsSize)
{//左指针与右指针相等时,往下走,不等时,填到左指针
if(nums[left] != nums[right])
{
left++;
nums[left] = nums[right];
}
right++;
}
return left + 1;//总长度算上左指针后面的
}
class Solution {//c++ public: int removeDuplicates(vector<int>& nums) { int n = nums.size(); if (n == 0) { return 0; } int fast = 1, slow = 1; while (fast < n) { if (nums[fast] != nums[fast - 1]) { nums[slow] = nums[fast]; ++slow; } ++fast; } return slow; } };
public int removeDuplicates(int[] nums) {//java
if(nums == null || nums.length == 0) return 0;
int p = 0;
int q = 1;
while(q < nums.length){
if(nums[p] != nums[q]){
nums[p + 1] = nums[q];
p++;
}
q++;
}
return p + 1;
}
给你一个有序数组 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
已按升序排列int removeDuplicates(int* nums, int numsSize){ if(numsSize <= 2) {//排除极端情况 return numsSize; } int left = 2, right = 2; while(right < numsSize) {//right指的是保留元素 if(nums[left - 2] != nums[right]) { nums[left] = nums[right];//第三个元素为缓冲 left++; } right++; } return left ; }
给定一个大小为 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
**进阶:**尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
class Solution { public: int majorityElement(vector<int>& nums) { sort(nums.begin(), nums.end()); return nums[nums.size() / 2]; } }; 投票算法: int majorityElement(int* nums, int numsSize){ int candidate=nums[0]; //初始候选元素 int count=1; for(int i=1;i<numsSize;i++){ if(nums[i]==candidate){ //与候选元素相同,票数加一 count++; } else{ //与候选元素不同 count--; if(count<0){ //若票数小于0,说明该元素不是候选元素 candidate=nums[i]; count=1; } } } return candidate; } c++版: class Solution { public: int majorityElement(vector<int>& nums) { int candidate=nums[0],count=0; for(int i=0;i<nums.size();i++){ if(count==0) candidate=nums[i]; if(nums[i]==candidate)count++; else count--; } return ans; } };
原理:1->2,3… 会多出来1,只有count<0时才会改变candidate的值,所以最后留下来的就是多数元素
class Solution {//java private Map<Integer, Integer> countNums(int[] nums) { Map<Integer, Integer> counts = new HashMap<Integer, Integer>(); for (int num : nums) { if (!counts.containsKey(num)) { counts.put(num, 1); } else { counts.put(num, counts.get(num) + 1); } } return counts; } public int majorityElement(int[] nums) { Map<Integer, Integer> counts = countNums(nums); Map.Entry<Integer, Integer> majorityEntry = null; for (Map.Entry<Integer, Integer> entry : counts.entrySet()) { if (majorityEntry == null || entry.getValue() > majorityEntry.getValue()) { majorityEntry = entry; } } return majorityEntry.getKey(); } }
给定一个整数数组 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
(复杂版) void rotate(int* nums, int numsSize, int k){ if(k > numsSize){ int a = k % numsSize; int b[numsSize]; for(int i = 0; i < numsSize; i++){ b[i] = nums[i]; } for(int i = 0; i < numsSize; i++){ if(i + a < numsSize){ nums[i + a] = b[i]; } else{ int ans = i + a - numsSize; nums[ans] = b[i]; } } } else{ int s[numsSize]; for(int i = 0; i < numsSize; i++){ s[i] = nums[i]; } for(int i = 0; i < numsSize; i++){ if(i + k < numsSize){ nums[i + k] = s[i]; } else{ int cnt = i + k - numsSize; nums[cnt] = s[i]; } } } } (标准版) void rotate(int* nums, int numsSize, int k) { int newArr[numsSize]; for (int i = 0; i < numsSize; ++i) { newArr[(i + k) % numsSize] = nums[i]; } for (int i = 0; i < numsSize; ++i) { nums[i] = newArr[i]; } }
class Solution {//c++
public:
void rotate(vector<int>& nums, int k) {
int n = nums.size();
vector<int> newArr(n);
for (int i = 0; i < n; ++i) {
newArr[(i + k) % n] = nums[i];
}
nums.assign(newArr.begin(), newArr.end());
}
};
class Solution {//java
public void rotate(int[] nums, int k) {
int n = nums.length;
int[] newArr = new int[n];
for (int i = 0; i < n; ++i) {
newArr[(i + k) % n] = nums[i];
}
System.arraycopy(newArr, 0, nums, 0, n);
}
}
给定一个数组 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
int maxProfit(int* prices, int pricesSize){
int minprice = 0x3f3f3f;
int maxprofit = 0;
for (int i = 0; i < pricesSize; i++) {
if (prices[i] < minprice) {
minprice = prices[i];
} else if (prices[i] - minprice > maxprofit) {
maxprofit = prices[i] - minprice;
}
}
return maxprofit;
}
class Solution {//c++
public:
int maxProfit(vector<int>& prices) {
int inf = 1e9;
int minprice = inf, maxprofit = 0;
for (int price: prices) {
maxprofit = max(maxprofit, price - minprice);
minprice = min(price, minprice);
}
return maxprofit;
}
};
public class Solution {//java
public int maxProfit(int prices[]) {
int minprice = Integer.MAX_VALUE;
int maxprofit = 0;
for (int i = 0; i < prices.length; i++) {
if (prices[i] < minprice) {
minprice = prices[i];
} else if (prices[i] - minprice > maxprofit) {
maxprofit = prices[i] - minprice;
}
}
return maxprofit;
}
}
给你一个整数数组 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
贪心:(只能计算最大利润,不能表示计算过程) class Solution { public: int maxProfit(vector<int>& prices) { int ans = 0; int n = prices.size(); for (int i = 1; i < n; ++i) { ans += max(0, prices[i] - prices[i - 1]); } return ans; } }; int maxProfit(int* prices, int pricesSize) { int ans = 0; for (int i = 1; i < pricesSize; ++i) { ans += fmax(0, prices[i] - prices[i - 1]); } return ans; } 动态规划: int maxProfit(int* prices, int pricesSize) { int dp[pricesSize][2]; dp[0][0] = 0, dp[0][1] = -prices[0]; for (int i = 1; i < pricesSize; ++i) { dp[i][0] = fmax(dp[i - 1][0], dp[i - 1][1] + prices[i]); dp[i][1] = fmax(dp[i - 1][1], dp[i - 1][0] - prices[i]); } return dp[pricesSize - 1][0]; }
class Solution {//c++
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
int dp[n][2];
dp[0][0] = 0, dp[0][1] = -prices[0];
for (int i = 1; i < n; ++i) {
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
}
return dp[n - 1][0];
}
};
class Solution {//java
public int maxProfit(int[] prices) {
int n = prices.length;
int[][] dp = new int[n][2];
dp[0][0] = 0;
dp[0][1] = -prices[0];
for (int i = 1; i < n; ++i) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
}
return dp[n - 1][0];
}
}
给你一个非负整数数组 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
bool canJump(int* nums, int numsSize){
int rightmost = 0;
for(int i = 0; i < numsSize; i++){
if(i <= rightmost){
rightmost = maxN(rightmost, i + nums[i]);//最远到达rightmost,仅表示位置
if(rightmost >= numsSize - 1){
return true;
}
}
}
return false;
}
int maxN(int a, int b){
return a > b ? a : b;
}
class Solution {//c++ public: bool canJump(vector<int>& nums) { int n = nums.size(); int rightmost = 0; for (int i = 0; i < n; ++i) { if (i <= rightmost) { rightmost = max(rightmost, i + nums[i]); if (rightmost >= n - 1) { return true; } } } return false; } };
public class Solution {//java
public boolean canJump(int[] nums) {
int n = nums.length;
int rightmost = 0;
for (int i = 0; i < n; ++i) {
if (i <= rightmost) {
rightmost = Math.max(rightmost, i + nums[i]);
if (rightmost >= n - 1) {
return true;
}
}
}
return false;
}
}
给定一个长度为 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]
法一:(贪心)反向找出发位置,先找到能到达最后一个数的下标,然后在此基础上,选择距离最后一个位置最远的位置
int jump(int* nums, int numsSize){
int ans = 0, pos = numsSize - 1;
while(pos > 0){
for(int i = 0; i < pos; i++){
if(i + nums[i] >= pos){
pos = i;
ans++;
break;
}
}
}
return ans;
}
法二:正向查找,选择下一步最远的位置。比如说[2,3,1,2,4,2,3],从下标0出发,可以到下标1和2,下标1能到的更远,所以第一步到下标1
class Solution { public: int jump(vector<int>& nums) { int maxPos = 0, n = nums.size(), end = 0, step = 0; for (int i = 0; i < n - 1; ++i) { if (maxPos >= i) { maxPos = max(maxPos, i + nums[i]); if (i == end) { end = maxPos; ++step; } } } return step; } };
class Solution {//java public int jump(int[] nums) { int length = nums.length; int end = 0; int maxPosition = 0; int steps = 0; for (int i = 0; i < length - 1; i++) { maxPosition = Math.max(maxPosition, i + nums[i]); if (i == end) { end = maxPosition; steps++; } } return steps; } }
给你一个整数数组 citations
,其中 citations[i]
表示研究者的第 i
篇论文被引用的次数。计算并返回该研究者的 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
法一:排序,先设h=0,遍历过程中citation[i]>h , 至少找到一篇引用了h+1次的论文,h++
class Solution {
public:
int hIndex(vector<int>& citations) {
sort(citations.begin(), citations.end());
int h = 0, i = citations.size() - 1;
while (i >= 0 && citations[i] > h) {
h++;
i--;
}
return h;
}
};
法二:计数排序
使用计数排序算法,新建并维护一个数组 counter 用来记录当前引用次数的论文有几篇。
根据定义,我们可以发现 H 指数不可能大于总的论文发表数,所以对于引用次数超过论文发表数的情况,我们可以将其按照总的论文发表数来计算即可。这样我们可以限制参与排序的数的大小为 [0,n](其中 n为总的论文发表数),使得计数排序的时间复杂度降低到 O(n)。
最后我们可以从后向前遍历数组 counter,对于每个 0≤i≤n,在数组 counter 中得到大于或等于当前引用次数 iii 的总论文数。当我们找到一个 H指数时跳出循环,并返回结果。
int hIndex(int *citations, int citationsSize) { int n = citationsSize, tot = 0; int counter[n + 1]; memset(counter, 0, sizeof(counter)); for (int i = 0; i < n; i++) { if (citations[i] >= n) { counter[n]++; } else { counter[citations[i]]++; } } for (int i = n; i >= 0; i--) { tot += counter[i]; if (tot >= i) { return i; } } return 0; }
法三:二分搜索
我们需要找到一个值 h,它是满足「有 h 篇论文的引用次数至少为 h」的最大值。小于等于 h 的所有值 x 都满足这个性质,而大于 h 的值都不满足这个性质。同时因为我们可以用较短时间(扫描一遍数组的时间复杂度为 O(n),其中 n为数组 citations 的长度)来判断 x 是否满足这个性质,所以这个问题可以用二分搜索来解决。
设查找范围的初始左边界 left为 0,初始右边界 right 为 n。每次在查找范围内取中点 mid,同时扫描整个数组,判断是否至少有 mid 个数大于 mid。如果有,说明要寻找的 h 在搜索区间的右边,反之则在左边。
int hIndex(int* citations, int citationsSize){ int left=0,right=citationsSize; int mid=0,cnt=0; while(left<right){ // +1 防止死循环 mid=(left+right+1)>>1; cnt=0; for(int i=0;i<citationsSize;i++){ if(citations[i]>=mid){ cnt++; } } if(cnt>=mid){ // 要找的答案在 [mid,right] 区间内 left=mid; }else{ // 要找的答案在 [0,mid) 区间内 right=mid-1; } } return left; }
实现RandomizedSet
类:
RandomizedSet()
初始化 RandomizedSet
对象bool insert(int val)
当元素 val
不存在时,向集合中插入该项,并返回 true
;否则,返回 false
。bool remove(int val)
当元素 val
存在时,从集合中移除该项,并返回 true
;否则,返回 false
。int getRandom()
随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。每个元素应该有 相同的概率 被返回。你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1)
。
示例:
输入
["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"]
[[], [1], [2], [2], [], [1], [2], []]
输出
[null, true, false, true, 2, true, false, 2]
解释
RandomizedSet randomizedSet = new RandomizedSet();
randomizedSet.insert(1); // 向集合中插入 1 。返回 true 表示 1 被成功地插入。
randomizedSet.remove(2); // 返回 false ,表示集合中不存在 2 。
randomizedSet.insert(2); // 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。
randomizedSet.getRandom(); // getRandom 应随机返回 1 或 2 。
randomizedSet.remove(1); // 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。
randomizedSet.insert(2); // 2 已在集合中,所以返回 false 。
randomizedSet.getRandom(); // 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。
提示:
-231 <= val <= 231 - 1
insert
、remove
和 getRandom
函数 2 * ``105
次getRandom
方法时,数据结构中 至少存在一个 元素。class RandomizedSet { public: RandomizedSet() { srand((unsigned)time(NULL)); } bool insert(int val) { if (indices.count(val)) { return false; } int index = nums.size(); nums.emplace_back(val); indices[val] = index; return true; } bool remove(int val) { if (!indices.count(val)) { return false; } int index = indices[val]; int last = nums.back(); nums[index] = last; indices[last] = index; nums.pop_back(); indices.erase(val); return true; } int getRandom() { int randomIndex = rand()%nums.size(); return nums[randomIndex]; } private: vector<int> nums; unordered_map<int, int> indices; };
class RandomizedSet { List<Integer> nums; Map<Integer, Integer> indices; Random random; public RandomizedSet() { nums = new ArrayList<Integer>(); indices = new HashMap<Integer, Integer>(); random = new Random(); } public boolean insert(int val) { if (indices.containsKey(val)) { return false; } int index = nums.size(); nums.add(val); indices.put(val, index); return true; } public boolean remove(int val) { if (!indices.containsKey(val)) { return false; } int index = indices.get(val); int last = nums.get(nums.size() - 1); nums.set(index, last); indices.put(last, index); nums.remove(nums.size() - 1); indices.remove(val); return true; } public int getRandom() { int randomIndex = random.nextInt(nums.size()); return nums.get(randomIndex); } }
给你一个整数数组 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)
的额外空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组 不被视为 额外空间。)
法一:L和R表示两侧的数组乘积列表,L[i]表示i左侧所有数字乘积 class Solution { public: vector<int> productExceptSelf(vector<int>& nums) { int length = nums.size(); // L 和 R 分别表示左右两侧的乘积列表 vector<int> L(length, 0), R(length, 0); vector<int> answer(length); // L[i] 为索引 i 左侧所有元素的乘积 // 对于索引为 '0' 的元素,因为左侧没有元素,所以 L[0] = 1 L[0] = 1; for (int i = 1; i < length; i++) { L[i] = nums[i - 1] * L[i - 1]; } // R[i] 为索引 i 右侧所有元素的乘积 // 对于索引为 'length-1' 的元素,因为右侧没有元素,所以 R[length-1] = 1 R[length - 1] = 1; for (int i = length - 2; i >= 0; i--) { R[i] = nums[i + 1] * R[i + 1]; } // 对于索引 i,除 nums[i] 之外其余各元素的乘积就是左侧所有元素的乘积乘以右侧所有元素的乘积 for (int i = 0; i < length; i++) { answer[i] = L[i] * R[i]; } return answer; } }; class Solution {//java public int[] productExceptSelf(int[] nums) { int length = nums.length; // L 和 R 分别表示左右两侧的乘积列表 int[] L = new int[length]; int[] R = new int[length]; int[] answer = new int[length]; // L[i] 为索引 i 左侧所有元素的乘积 // 对于索引为 '0' 的元素,因为左侧没有元素,所以 L[0] = 1 L[0] = 1; for (int i = 1; i < length; i++) { L[i] = nums[i - 1] * L[i - 1]; } // R[i] 为索引 i 右侧所有元素的乘积 // 对于索引为 'length-1' 的元素,因为右侧没有元素,所以 R[length-1] = 1 R[length - 1] = 1; for (int i = length - 2; i >= 0; i--) { R[i] = nums[i + 1] * R[i + 1]; } // 对于索引 i,除 nums[i] 之外其余各元素的乘积就是左侧所有元素的乘积乘以右侧所有元素的乘积 for (int i = 0; i < length; i++) { answer[i] = L[i] * R[i]; } return answer; } } 法二:直接将L作为answer数组 class Solution { public: vector<int> productExceptSelf(vector<int>& nums) { int length = nums.size(); vector<int> answer(length); // answer[i] 表示索引 i 左侧所有元素的乘积 // 因为索引为 '0' 的元素左侧没有元素, 所以 answer[0] = 1 answer[0] = 1; for (int i = 1; i < length; i++) { answer[i] = nums[i - 1] * answer[i - 1]; } // R 为右侧所有元素的乘积 // 刚开始右边没有元素,所以 R = 1 int R = 1; for (int i = length - 1; i >= 0; i--) { // 对于索引 i,左边的乘积为 answer[i],右边的乘积为 R answer[i] = answer[i] * R; // R 需要包含右边所有的乘积,所以计算下一个结果时需要将当前值乘到 R 上 R *= nums[i]; } return answer; } }; class Solution {//java public int[] productExceptSelf(int[] nums) { int length = nums.length; int[] answer = new int[length]; // answer[i] 表示索引 i 左侧所有元素的乘积 // 因为索引为 '0' 的元素左侧没有元素, 所以 answer[0] = 1 answer[0] = 1; for (int i = 1; i < length; i++) { answer[i] = nums[i - 1] * answer[i - 1]; } // R 为右侧所有元素的乘积 // 刚开始右边没有元素,所以 R = 1 int R = 1; for (int i = length - 1; i >= 0; i--) { // 对于索引 i,左边的乘积为 answer[i],右边的乘积为 R answer[i] = answer[i] * R; // R 需要包含右边所有的乘积,所以计算下一个结果时需要将当前值乘到 R 上 R *= nums[i]; } return answer; } }
在一条环路上有 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
class Solution { public: int canCompleteCircuit(vector<int>& gas, vector<int>& cost) { int n = gas.size(); int i = 0;//i是gas的位置遍历查找是否有合适的 while (i < n) { int sumOfGas = 0, sumOfCost = 0; int cnt = 0;//cnt是从计数能到达的下一个索引 while (cnt < n) { int j = (i + cnt) % n;//取模用来表示到底后回来 sumOfGas += gas[j]; sumOfCost += cost[j]; if (sumOfCost > sumOfGas) {//消耗大于加的油 break;//跳出 } cnt++;//否则就+1 } if (cnt == n) {//能到达的索引数量达到n return i;//i处就是起点 } else { i = i + cnt + 1;//cnt处是无法通往下一站的,在此基础+1 } } return -1; } };
class Solution {//java版 public int canCompleteCircuit(int[] gas, int[] cost) { int n = gas.length; int i = 0; while (i < n) { int sumOfGas = 0, sumOfCost = 0; int cnt = 0; while (cnt < n) { int j = (i + cnt) % n; sumOfGas += gas[j]; sumOfCost += cost[j]; if (sumOfCost > sumOfGas) { break; } cnt++; } if (cnt == n) { return i; } else { i = i + cnt + 1; } } return -1; } }
罗马数字包含以下七种字符: 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]
内class Solution { private: unordered_map<char, int> symbolValues = {//设置哈希表 {'I', 1},//用逗号隔开 {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}, };//分号结尾 public: int romanToInt(string s) { int ans = 0; int n = s.length(); for (int i = 0; i < n; ++i) { int value = symbolValues[s[i]]; if (i < n - 1 && value < symbolValues[s[i + 1]]) { ans -= value;//罗马数字左边小于右边代表相减 } else { ans += value;//一般情况就是右边小于左边就简单相加 } } return ans; } };
class Solution { Map<Character, Integer> symbolValues = new HashMap<Character, Integer>() {{ put('I', 1);//用分号隔开 put('V', 5); put('X', 10); put('L', 50); put('C', 100); put('D', 500); put('M', 1000); }}; public int romanToInt(String s) { int ans = 0; int n = s.length(); for (int i = 0; i < n; ++i) { int value = symbolValues.get(s.charAt(i)); if (i < n - 1 && value < symbolValues.get(s.charAt(i + 1))) { ans -= value; } else { ans += value; } } return ans; } }
罗马数字包含以下七种字符: 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
法一:哈希表 const pair<int, string> valueSymbols[] = {//端点值和特殊值 {1000, "M"}, {900, "CM"}, {500, "D"}, {400, "CD"}, {100, "C"}, {90, "XC"}, {50, "L"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"}, }; class Solution { public: string intToRoman(int num) { string roman; for (const auto &[value, symbol] : valueSymbols) { while (num >= value) {//按照大小顺序依次减 num -= value; roman += symbol;//字符串按照此顺序加上 } if (num == 0) {//减完后退出 break; } } return roman; } }; //击败100% class Solution { public: string intToRoman(int num) { int value[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9 , 5, 4, 1}; string reps[] = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; string ans = ""; for (int i = 0; i < 13; i++) { while (num >= value[i]) { num -= value[i]; ans += reps[i]; } } return ans; } };
class Solution { 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(); } } class Solution { public String intToRoman(int num) { StringBuilder sb = new StringBuilder(); while (num > 0) { if (num >= 1000) { int total = num / 1000; if (total > 0) { for (int j = 0; j < total; j++) { sb.append("M"); num -= 1000; } } } // 900 特殊处理 if (num >= 900) { sb.append("CM"); num -= 900; } if (num >= 500) { int total = num / 500; if (total > 0) { for (int j = 0; j < total; j++) { sb.append("D"); num -= 500; } } } // 400 特殊处理 if (num >= 400) { sb.append("CD"); num -= 400; } if (num >= 100) { int total = num / 100; if (total > 0) { for (int j = 0; j < total; j++) { sb.append("C"); num -= 100; } } } // 90 特殊处理 if (num >= 90) { sb.append("XC"); num -= 90; } if (num >= 50) { int total = num / 50; if (total > 0) { for (int j = 0; j < total; j++) { sb.append("L"); num -= 50; } } } // 40 特殊处理 if (num >= 40) { sb.append("XL"); num -= 40; } if (num >= 10) { int total = num / 10; if (total > 0) { for (int j = 0; j < total; j++) { sb.append("X"); num -= 10; } } } // 9 特殊处理 if (num >= 9) { sb.append("IX"); num -= 9; } if (num >= 5) { int total = num / 5; if (total > 0) { for (int j = 0; j < total; j++) { sb.append("V"); num -= 5; } } } // 4 特殊处理 if (num >= 4) { sb.append("IV"); num -= 4; } if (num >= 1) { int total = num / 1; if (total > 0) { for (int j = 0; j < total; j++) { sb.append("I"); num -= 1; } } } } return sb.toString(); } }
法二:按位拼接 const string thousands[] = {"", "M", "MM", "MMM"}; const string hundreds[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; const string tens[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; const string ones[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; class Solution { public: string intToRoman(int num) { return thousands[num / 1000] + hundreds[num % 1000 / 100] + tens[num % 100 / 10] + ones[num % 10]; } }; class Solution { String[] thousands = {"", "M", "MM", "MMM"}; String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; public String intToRoman(int num) { StringBuffer roman = new StringBuffer(); roman.append(thousands[num / 1000]); roman.append(hundreds[num % 1000 / 100]); roman.append(tens[num % 100 / 10]); roman.append(ones[num % 10]); return roman.toString(); } }
给你一个字符串 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
中至少存在一个单词//终于碰到简单题了哈哈,击败100% class Solution { public: int lengthOfLastWord(string s) { int cnt = 0; int index = 0; for(int i = s.length() - 1; i >= 0; i--){ if(s[i] == ' '){ continue; } else{ index = i; break; } } for(int i = index; i >= 0; i--){ if(s[i] != ' '){ cnt++; } else{ break; } } return cnt; } };
class Solution { public int lengthOfLastWord(String s) { int cnt = 0; int index = 0; for(int i = s.length() - 1; i >= 0; i--){ if(s.charAt(i) == ' '){ continue; } else{ index = i; break; } } for(int i = index; i >= 0; i--){ if(s.charAt(i) != ' '){ cnt++; } else{ break; } } return cnt; } }
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""
。
示例 1:
输入:strs = ["flower","flow","flight"]
输出:"fl"
示例 2:
输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。
提示:
1 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i]
仅由小写英文字母组成class Solution { public: string longestCommonPrefix(vector<string>& strs) { int len=strs.size(); string ans; int vislength; int vis=0;//记录是否发现第一个不满足的条件 int maxx=0;//获取字符串数组中最大的字符串的长度 for(int i=0;i<len;i++) { maxx=max(maxx,(int)strs[i].size()); //寻找字符串数组中最大字符串长度,防止越界 } for(int i=0;i<maxx;i++){ for(int j=0;j<len-1;j++) { if(strs[j][i] != strs[j+1][i]) //寻找不等元素直接跳出所有循环,锁定答案 { vislength=i; vis=1; break; } } if(vis!=0 ) break; } for(int i=0;i<vislength;i++) ans+=strs[0][i];//任意其中某个字符串的前vislength项 return ans; } };
class Solution { public String longestCommonPrefix(String[] strs) { if(strs.length == 0){ return ""; } String ans = strs[0];//初始化,结果字符串为字符串数组的第一个 for(int i = 1; i < strs.length; i++){//遍历字符串数组 int j = 0;//跳出循环之后还需要用到j for(; j < ans.length() && j < strs[i].length(); j++){//遍历数组里面的字符串 if(ans.charAt(j) != strs[i].charAt(j)){ break;//不等时跳出 } } ans = ans.substring(0, j); if(ans.equals("")){ return ans; } } return ans; } } //使用辅助函数 class Solution { public String longestCommonPrefix(String[] strs) { String pattern = strs[0]; for (int i = 1; i < strs.length; i++) { //比较当前字符串与最长公共前缀,更新最长公共前缀 pattern = longestCommonHelper(pattern, strs[i]); if ("".equals(pattern)) { return pattern; } } return pattern; } //辅助函数用于获取两个字符串的公共前缀 private String longestCommonHelper(String str1, String str2) { int i = 0, j = 0; int len = Math.min(str1.length(), str2.length()); while (i < len) { if (str1.charAt(i) != str2.charAt(j)) { break; } i++; j++; } return str1.substring(0, i); } }
给你一个字符串 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)
额外空间复杂度的 原地 解法。
//击败100% class Solution { public: string reverseWords(string s) { // 使用双指针 int m = s.size() - 1; string res; // 除去尾部空格 while (s[m] == ' ' && m > 0) m--; int n = m; // n是另一个指针 while (m >= 0) { while (m >= 0 && s[m] != ' ') m--; res += s.substr(m + 1, n - m) + " "; // 获取单词并加上空格 while (m >= 0 && s[m] == ' ') m--; n = m; }//s.substr(a, b)返回string从a开始的b个字符 return res.substr(0, res.size() - 1); // 忽略最后一位的空格 } };
class Solution { public String reverseWords(String s) { int start, end; // 每个单词的开始和结束索引(左闭右开) StringBuilder sb = new StringBuilder(); for (int i = s.length() - 1; i >= 0; i--) { if (s.charAt(i) == ' ') continue; //跳过空格 end = i + 1; //找到结束索引 while (i >= 0 && s.charAt(i) != ' ') i--; //选取单词 start = i + 1; //找到开始索引 for (int j = start; j < end; j++) //将每个单词按开始结束索引赋值到StringBuilder sb.append(s.charAt(j)); sb.append(' '); //加上单词间的空格 } sb.deleteCharAt(sb.length() - 1); //删掉最后一个多余的空格 return sb.toString(); } }
//使用库,不推荐
class Solution {
public String reverseWords(String s) {
s = s.trim();//除去开头和结尾的空格
List<String> list = Arrays.asList(s.split("\\s+"));//正则表达式s表示多个空格(大写非空格)
Collections.reverse(list);
return String.join(" ", list);//join将字符串数组拼成一个字符串
}
}
将一个给定字符串 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
设 n为字符串 s的长度,r=numRows。对于 r=1(只有一行)或者 r≥n(只有一列)的情况,答案与 s相同,我们可以直接返回 s。对于其余情况,考虑创建一个二维矩阵,然后在矩阵上按 Z 字形填写字符串 s,最后逐行扫描矩阵中的非空字符,组成答案。
根据题意,当我们在矩阵上填写字符时,会向下填写 r个字符,然后向右上继续填写 r−2个字符,最后回到第一行,因此 Z 字形变换的周期 t=r+r−2=2r−2,每个周期会占用矩阵上的 1+r−2=r−1 列。
因此我们有 ⌈n / t ⌉ 个周期(最后一个周期视作完整周期),乘上每个周期的列数,得到矩阵的列数 c=⌈n/t⌉⋅(r−1)。
创建一个 r行 c列的矩阵,然后遍历字符串 s并按 Z 字形填写。具体来说,设当前填写的位置为 (x,y),即矩阵的 x行 y列。初始 (x,y)=(0,0),即矩阵左上角。若当前字符下标 i满足 i mod t < r−1,则向下移动,否则向右上移动。
法一: class Solution { public: string convert(string s, int numRows) { int n = s.length(), r = numRows; if (r == 1 || r >= n) { return s;//一列或者字符串长度小于行数就是本身 } vector<string> mat(r);//设置行数长度的二维矩阵 for (int i = 0, x = 0, t = r * 2 - 2; i < n; ++i) { mat[x] += s[i];//竖着添加字符 i % t < r - 1 ? ++x : --x;//字符串索引对周期取模小于一个周期占的列,二维矩阵的列索引就+1,否则-1 } string ans; for (auto &row : mat) { ans += row; } return ans; } }; class Solution { public String convert(String s, int numRows) { int n = s.length(), r = numRows; if (r == 1 || r >= n) { return s; } StringBuffer[] mat = new StringBuffer[r]; for (int i = 0; i < r; ++i) { mat[i] = new StringBuffer(); } for (int i = 0, x = 0, t = r * 2 - 2; i < n; ++i) { mat[x].append(s.charAt(i)); if (i % t < r - 1) { ++x; } else { --x; } } StringBuffer ans = new StringBuffer(); for (StringBuffer row : mat) { ans.append(row); } return ans.toString(); } }
法二: class Solution { public: string convert(string s, int numRows) { int n = s.length(), r = numRows; if (r == 1 || r >= n) { return s; } string ans; int t = r * 2 - 2; for (int i = 0; i < r; ++i) { // 枚举矩阵的行 for (int j = 0; j + i < n; j += t) { // 枚举每个周期的起始下标 ans += s[j + i]; // 当前周期的第一个字符 if (0 < i && i < r - 1 && j + t - i < n) { ans += s[j + t - i]; // 当前周期的第二个字符 } } } return ans; } };
给你两个字符串 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
仅由小写英文字符组成//终于遇到简单题了哈哈,击败100%,需要注意的是needle可能是单个字母,边界值需注意 class Solution { public: int strStr(string haystack, string needle) { if(haystack == needle){ return 0; } if(haystack.length() < needle.length()){ return -1; } int cnt = 1; for(int i = 0; i < haystack.length(); i++){ if(needle[0] == haystack[i] && i + needle.length() <= haystack.length()){ int t = i + 1; for(int j = 1; j < needle.length(); t++, j++){ if(needle[j] != haystack[t]){ break; } else{ cnt++; } } if(cnt == needle.length()){ return i; } cnt = 1; } } return -1; } };
//此方法java版 class Solution { public int strStr(String haystack, String needle) { if(haystack == needle){ return 0; } if(haystack.length() < needle.length()){ return -1; } int cnt = 1; for(int i = 0; i < haystack.length(); i++){ if(needle.charAt(0) == haystack.charAt(i) && i + needle.length() <= haystack.length()){ int t = i + 1; for(int j = 1; j < needle.length(); j++, t++){ if(needle.charAt(j) != haystack.charAt(t)){ break; } else{ cnt++; } } if(cnt == needle.length()){ return i; } cnt = 1; } } return -1; } } //java击败100% class Solution { public int strStr(String haystack, String needle) { return kmp(haystack,needle); } public static int kmp(String s1, String s2){ char[] c1 = s1.toCharArray(); char[] c2 = s2.toCharArray(); int n = c1.length; int m = c2.length; int[] next = nestArray(c2,m); int x = 0;//c1的比较位置 int y = 0;//c2的比较位置 while(x < n && y <m){ if(c1[x] == c2[y]){ x++;y++;//相等时同时遍历 }else if(y == 0){//当y一直到0时都不相等则只能重新选择x x++; }else{ //不相等时,跳转到next处 y = next[y]; } } if(y == m){ return x-y; }else{ return -1; } } public static int[] nestArray(char [] s, int m){ int[] next = new int[m]; next[0] = -1; if(m == 1){ return next; } next[1] = 0; for (int i = 2; i < m; i++) { char before = s[i-1];//当前的前一个字符 int cur = next[i-1];//需要比较的那个字符的索引 char low = s[cur];//需要比较的字符 while(cur > 0 && low != before){//如果前一个和比较的不相同就不断更新直到相同/跳转到头了才退出循环 cur = next[cur]; low = s[cur]; } if(before == low){//如果退出循环后是比较结果相等则给next赋值 next[i] = cur + 1; } if(cur == 0){//不相等时判断是否跳转到0,如果和0处的相等则给next赋值为1,否则赋值为0 if(s[cur] == before){next[i] = 1;} else{next[i] =0;} } } return next; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。