文章目录
27. 移除元素
数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
暴力求解的时间复杂度为 O ( n 2 ) O(n^2) O(n2),双指针可以变为 O ( n ) O(n) O(n)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int flag = 0;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] != val) {
nums[flag] = nums[i];
flag++;
}
}
return flag;
}
};
26. 删除有序数组中的重复项
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int slowIndex = 0;
for (int fastIndex = 1; fastIndex < nums.size(); fastIndex++) {
if (nums[slowIndex] != nums[fastIndex]) {
slowIndex++;
nums[slowIndex] = nums[fastIndex];
}
}
return ++slowIndex;
}
};
通用解法
为了让解法更具有一般性,我们将原问题的「最多保留 1 位」修改为「最多保留 k 位」。
对于此类问题,我们应该进行如下考虑:
由于是保留 k 个相同数字,对于前 k 个数字,我们可以直接保留。
对于后面的任意数字,能够保留的前提是:与当前写入的位置前面的第 k 个元素进行比较,不相同则保留。
举个🌰,我们令 k=1,假设有样例:[3,3,3,3,4,4,4,5,5,5]
- 设定变量 idx,指向待插入位置。idx 初始值为 0,目标数组为 []
- 首先我们先让第 1 位直接保留(性质 1)。idx 变为 1,目标数组为 [3]
- 继续往后遍历,能够保留的前提是与 idx 的前面 1 位元素不同(性质 2),因此我们会跳过剩余的 3,将第一个 4 追加进去。idx 变为 2,目标数组为 [3,4]
- 继续这个过程,跳过剩余的 4,将第一个 5 追加进去。idx 变为 3,目标数组为 [3,4,5]
- 当整个数组被扫描完,最终我们得到了目标数组 [3,4,5] 和 答案 idx 为 3。
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
return process(nums,1);
}
int process(vector<int>& nums,int k){
int idx = 0;
for(auto x : nums){
if(idx < k or nums[idx - k] != x){
nums[idx++] = x;
}
}
return idx;
}
};
283. 移动零
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
if (nums[fastIndex] != 0) {
nums[slowIndex++] = nums[fastIndex];
}
}
while (slowIndex < nums.size()) {
nums[slowIndex++] = 0;
}
}
};
减少操作次数:左指针寻找第一个0值,右指针寻找第一个非0值,交换。
344. 反转字符串
class Solution {
public:
void reverseString(vector<char>& s) {
for (int left = 0, right = s.size() - 1; left < right; left++, right--) {
swap(s[left], s[right]);
}
}
};
swap这种简单的函数可以直接用
844. 比较含退格的字符串
1. 重构字符串
复杂度分析
时间复杂度:O(N+M)
,其中 N 和 M 分别为字符串 S 和 T 的长度。我们需要遍历两字符串各一次。
空间复杂度:O(N+M)
,其中 N 和 M 分别为字符串 S 和 T 的长度。主要为还原出的字符串的开销。
class Solution {
public:
bool backspaceCompare(string S, string T) {
return build(S) == build(T);
}
string build(string str) {
string ret;
for (char ch : str) {
if (ch != '#') {
ret.push_back(ch);
} else if (!ret.empty()) {
ret.pop_back();
}
}
return ret;
}
};
2. 双指针
class Solution {
public:
bool backspaceCompare(string S, string T) {
int i = S.length() - 1, j = T.length() - 1;
int skipS = 0, skipT = 0;
while (i >= 0 || j >= 0) {
while (i >= 0) {
if (S[i] == '#') {
skipS++, i--;
} else if (skipS > 0) {
skipS--, i--;
} else {
break;
}
}
while (j >= 0) {
if (T[j] == '#') {
skipT++, j--;
} else if (skipT > 0) {
skipT--, j--;
} else {
break;
}
}
if (i >= 0 && j >= 0) {
if (S[i] != T[j]) {
return false;
}
} else {
if (i >= 0 || j >= 0) {
return false;
}
}
i--, j--;
}
return true;
}
};
977. 有序数组的平方
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int flag = -1;
vector<int> ans;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] <= 0) {
flag = i;
} else {
break;
}
}
int left = flag, right = flag + 1;
while (left >= 0 || right < nums.size()) {
if (left < 0) {
ans.push_back(nums[right] * nums[right]);
right++;
} else if (right == nums.size()) {
ans.push_back(nums[left] * nums[left]);
left--;
} else if (nums[left] * nums[left] < nums[right] * nums[right]){
ans.push_back(nums[left] * nums[left]);
left--;
} else {
ans.push_back(nums[right] * nums[right]);
right++;
}
}
return ans;
}
};
- 先找到正负数交接的位置,然后向两边扩展(并比较大小)
80. 删除有序数组中的重复项 II
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int slowIndex = 0, k = 2;
for (auto x: nums){
if(slowIndex < k or nums[slowIndex - k] != x) {
nums[slowIndex++] = x;
}
}
return slowIndex;
}
};
5. 最长回文子串
class Solution {
public:
string palindrome(string s, int left, int right) {
while (left >= 0 && right < s.size() && s[left] == s[right]) {
left--;
right++;
}
return s.substr(left + 1, right - left - 1);
}
string longestPalindrome(string s) {
int res = 0;
string ans;
for (int i = 0; i < s.size(); i++) {
string s1 = palindrome(s, i, i);
string s2 = palindrome(s, i, i + 1);
int cnt1 = s1.size();
int cnt2 = s2.size();
if (cnt1 > res) {
res = cnt1;
ans = s1;
}
if (cnt2 > res) {
res = cnt2;
ans = s2;
}
}
return ans;
}
};
- 从中间向两边找
557. 反转字符串中的单词 III
1. 没用双指针
class Solution {
public:
string reverseWords(string s) {
int cnt = 0;
string s1 = "", ans = "";
for (int i = 0; i <= s.size(); i++) {
if (i == s.size() or s[i] == ' ') {
ans += s1;
s1 = "";
if (i != s.size()) {
ans += ' ';
}
} else {
s1 = s[i] + s1;
}
}
return ans;
}
};
-
时间复杂度:
O(N)
,其中 NN 为字符串的长度。原字符串中的每个字符都会在O(1)
的时间内放入新字符串中。 -
空间复杂度:
O(N)
。我们开辟了与原字符串等大的空间。
2. 双指针
class Solution {
public:
string reverseWords(string s) {
int length = s.length();
int i = 0;
while (i < length) {
int start = i;
while (i < length && s[i] != ' ') {
i++;
}
int left = start, right = i - 1;
while (left < right) {
swap(s[left], s[right]);
left++;
right--;
}
while (i < length && s[i] == ' ') {
i++;
}
}
return s;
}
};
-
时间复杂度:
O(N)
。字符串中的每个字符要么在O(1)
的时间内被交换到相应的位置,要么因为是空格而保持不动。 -
空间复杂度:
O(1)
。因为不需要开辟额外的数组。