一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24天,点击查看活动详情。
剑指 Offer 57. 和为s的两个数字
思路
(双指针) O(n)
具体过程如下:
1、初始化两个指针 i , j 分别指向数组nums 的左右两端。
2、循环搜索,当双指针相遇时跳出:
- 计算
s = nums[i] + nums[j]; - 如果
s < target,则指针i向右移动; - 如果
s > target,则指针j向左移动; - 如果
s == target,则返回{nums[i],nums[j]};
3、最后返回空数组,代表没有答案。
时间复杂度分析: O(n)。
c++代码
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int i = 0, j = nums.size() - 1;
while(i < j){
if(nums[i] + nums[j] < target) i++;
else if(nums[i] + nums[j] > target) j--;
else return {nums[i],nums[j]};
}
return {};
}
};
(哈希) O(n)
具体过程如下:
-
1、定义一个
hash表,用来记录nums数组中每个数出现的次数 -
2、遍历整个
nums数组,对于当前遍历的数字nums[i],我们先在hash表中查找target - nums[i]是否存在。- 如果存在:直接返回
{target - nums[i], nums[i]}。 - 否则,将
nums[i]加入hash表中 。
- 如果存在:直接返回
时间复杂度分析: O(n)。
c++代码
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> res;
unordered_set<int> hash;
for(int x : nums){
if(hash.count(target - x)){
res = vector<int>{target - x, x};
return res;
}
hash.insert(x);
}
return res;
}
};
剑指 Offer 57 - II. 和为s的连续正数序列
思路
(双指针) O(n)
我们定义两个指针i和j指针,将区间[i,j]看成滑动窗口,那么两个指针就分别表示滑动窗口的开始位置和结束位置,同时我们再维护一个sum变量用来存贮区间[j,i]连续序列的的和。通过不断调整两个指针来使得滑动窗口维护的区间和sum等于target,并记录答案。
过程如下:
- 1、我们定义两个指针
i,j,初始化i = 1,j = 1,让两个指针都指向连续正数序列的开头,i指针用于扩展窗口,j指针用于收缩窗口。 - 2、枚举整个正数序列,当
sum < target时,我们可以不断增加j使得滑动窗口向右扩展,同时sum += j,即j++, sum += j。 - 3、当
sum == target并且滑动窗口的长度大于1时,将滑动窗口中的数记录到res中。 - 4、我们记录完一次合法的方案以后,就可以向右收缩滑动窗口,进行下一次合法方案的查找,即
sum -= i, i++。
整个序列只需要枚举到target/2,即target的一半。
时间复杂度分析: O(n)。
c++代码
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
vector<vector<int>> res;
int sum = 1;
for(int i = 1, j = 1; i <= target / 2; i++){
while(sum < target) j++, sum += j;
if(sum == target && j - i + 1 > 1){
vector<int> path;
for(int k = i; k <= j; k++) path.push_back(k);
res.push_back(path);
}
sum -= i;
}
return res;
}
};
剑指 Offer 58 - I. 翻转单词顺序
思路
对于样例 "the sky is blue"分两步操作:
- 1、将字符串中的每个单词逆序,样例输入变为:
"eht yks si eulb"; - 2、将整个字符串逆序,样例输入变为:
"blue is sky the";
图示样例过程:
1、将i和j指针指向字符串的开头,并让j指针跳过字符串s单词的前导空格,指向单词的首非空字符。
2、再让i和j指针指向同一个位置,并让j指针跳过若干个非空字符指向单词后的第一个空格。
3、将i和j指针之间的单词翻转。
4、我们将翻转后的单词重新赋值给字符串s的前若干个字符(长度为单词的大小),重复上述过程。
5、最后将整个字符串翻转。
细节:
-
赋值之后要给每个单词后补个空格作为单词之间的分隔符。
-
最后要将字符串
s之后多余的空格擦除。
- 具体实现细节看代码。
时间复杂度分析: 整个字符串总共扫描两遍,所以时间复杂度是 O(n)。且每次翻转一个字符串时,可以用两个指针分别从两端往中间扫描,每次交换两个指针对应的字符,所以额外空间的复杂度是 O(1)。
c++代码
class Solution {
public:
string reverseWords(string s) {
int k = 0;
for(int i = 0; i < s.size(); i++)
{
int j = i;
while(j < s.size() && s[j] == ' ') j++; //j指针跳过单词的前导空格
if(j == s.size()) break; //这里break是为了保证最后不会s[k++] = ' ',避免行首多加一个空格
i = j;
while(j < s.size() && s[j] != ' ') j++;
reverse(s.begin() + i, s.begin() + j);
if(k) s[k++] = ' '; //补个空格
while( i < j) s[k++] = s[i++];
}
s.erase(s.begin() + k,s.end());//擦除多余的空格
reverse(s.begin(),s.end());
return s;
}
};