这类题为什么叫数组追赶?
- 有序数组的平方
- 思路
法一:先对nums每个元素进行平方,再sort排序
法二:找到nums中负数和正数的分界线,进行类似与归并排序进行排序。[0,divid]为负数,平方后递减;[divid+1,len-1]为正数,平方后递增。相当于有了两个有序数组,用两个指针分别指向前一个数组最后面元素和后一个数组最前面的元素,均为两个数组中最小的元素,依次比较,选较小的放入新数组中,并且移动指针,当某个指针移出边界后,令一个指针还没遍历到的依次放入新数组就可。
法三:左右指针,nums是非递减数组,所以下标为0和下标为len-1的元素平方最大,左右指针分别指向他们,依次往中间收缩。所以在res数组中,得从下标为len-1开始存入数据 - 代码
法一:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int len = nums.size();
for(int i = 0;i<len;++i){
nums[i] *= nums[i];
}
sort(nums.begin(),nums.end());
return nums;
}
};
法二
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int len = nums.size();
int divid = -1;
int negative = -1;
int positive = 0;
vector<int> res;
for(int i = 0;i<len;++i){
if(nums[i]<0) divid = i;
else break;
}
negative = divid;
positive = divid+1;
while(negative>=0 || positive<len){
if(negative < 0) {
res.push_back(nums[positive]*nums[positive]);
++positive;
}
else if(positive == len){
res.push_back(nums[negative]*nums[negative]);
--negative;
}
else if(nums[positive]*nums[positive] < nums[negative]*nums[negative]){
res.push_back(nums[positive]*nums[positive]);
++positive;
}else{
res.push_back(nums[negative]*nums[negative]);
--negative;
}
}
return res;
}
};
法三:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int len = nums.size();
int left = 0,right = len-1,pos = len-1;
vector<int> res(len);
while(left<=right){
if(nums[left]*nums[left] > nums[right]*nums[right]){
res[pos] = nums[left]*nums[left];
++left;
}else{
res[pos] = nums[right]*nums[right];
--right;
}
--pos;
}
return res;
}
};
- 总结学习
总结一下各种排序算法:
使用双指针时可以把一个数组看成两个分开的数组,每个指针代表一个数组。
1:归并排序 (未完。。)
- 反转字符串
- 思路
自己想出来了 - 代码
class Solution {
public:
void reverseString(vector<char>& s) {
int len = s.size();
int left = 0,right = len-1;
char temp;
while(left <= right){
temp = s[left];
s[left] = s[right];
s[right] = temp;
++left;
--right;
}
}
};
- 颠倒字符串中的单词
代码:
- 没有利用原地实现
class Solution {
public:
string reverseWords(string s) {
int len = s.size();
string res;
string temp;
int i,j;
for(i = 0;i<len;++i){
if(s[i] != ' '){
for(j = i+1;j<len;++j){
if(s[j] == ' ') break;
}
temp = s.substr(i,j-i);
res = temp + " " + res;
i = j;
}
}
int a = res.size();
return res.substr(0,a-1);
}
};
- 原地算法:没想出来
- idx代表新候选字符串下标,start和end表示s字符串下标
- 当s[start]不是空格时,先判断idx是否为零,如果为零则表示候选数组中还没有字符,否者就是处理完一个单词,既有单词在候选字符串中,需要在单词后加一个空格
- start~end表示单词长度,为什么要s[idx++] = s[end++];,而不是单纯把下标++?因为下面对单词反转是按照候选字符串的下标进行反转
循环完之后,非空字符都处理完了,需要把后面的空字符去掉(反转前字符串前的空字符) - 函数都是前闭后开。
class Solution {
public:
string reverseWords(string s) {
// 反转整个字符串
reverse(s.begin(), s.end());
int n = s.size();
int idx = 0;
for (int start = 0; start < n; ++start) {
if (s[start] != ' ') {
// 填一个空白字符然后将idx移动到下一个单词的开头位置
if (idx != 0) s[idx++] = ' ';
// 循环遍历至单词的末尾
int end = start;
while (end < n && s[end] != ' ') s[idx++] = s[end++];
// 反转整个单词
reverse(s.begin() + idx - (end - start), s.begin() + idx);
// 更新start,去找下一个单词
start = end;
}
}
s.erase(s.begin() + idx, s.end());
return s;
}
};
- 通过删除字母匹配到字典里最长单词
- 思路
法一双指针: 一个sp指针指字符串s,另一个dp指针值dictionary中的字符串d。当指针对应字符相等时,sp dp同时++,否者sp++;当dp移动到尾部时,说明d时s的子串。
法二 先对dic进行长度降序和字母序升序排序,然后从前往后遍历找到第一个符合的直接返回。
法三:动态规划?????????? - 代码
class Solution {
public:
string findLongestWord(string s, vector<string>& dictionary) {
int len = dictionary.size();
string res,temp;
int sp = 0,dp = 0;
for(int i = 0;i<len;++i){
int len2 = dictionary[i].size();
while(sp<s.size() && dp<len2){
if(s[sp] == dictionary[i][dp]){
dictionary[i][dp++] = s[sp++];
} else{
++sp;
}
}
if(dp == len2){
if(dp > res.size() ||
(dp == res.size() && dictionary[i].compare(res)<0)){
res = dictionary[i];
}
}
dp = 0;
sp = 0;
}
return res;
}
};
核心在while循环中,相当于把d当成上图中的下面的容器,dp为idx的类原地算法。
下面判断时,首先看是否是子串,然后判断长度大于 或者 长度相等但是字符序较小。
- 总结学习
1:双指针+原地算法做的时候一定要画图,重点在判断条件上,确定好判断条件,确定好候选容器。根据判断条件双指针不是指向同一个容器。
2:string 的compare函数:
- 有效三角形的个数
- 思路
法一:排序后,遍历,对于两数之和,在后续的数组中查找小于两数之和的最大元素
法二:排序后,遍历i,j k为双指针 nums[k]<nums[i]+nums[j]的k的最大
细节:数组是非负的,可能有零,i,j对应元素都为0的话,[j+1~len-1]都是大于nums[i]+nums[j],二分法失败,解决办法是令k=j;
同样,法二也会有不存在满足 nums[k]<nums[i]+nums[j] 的下标的情况,这样的话,不让k出现在j的右侧就可以,累加是把 k-j与0的较大值累加到res - 代码
class Solution {
public:
int triangleNumber(vector<int>& nums) {
int n = nums.size();
sort(nums.begin(), nums.end());
int ans = 0;
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
int left = j + 1, right = n - 1, k = j;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] < nums[i] + nums[j]) {
k = mid;//仔细品 把这个小于的元素先保存下来
left = mid + 1;
}
else {
right = mid - 1;
}
}
ans += k - j;
}
}
return ans;
}
};
class Solution {
public:
int triangleNumber(vector<int>& nums) {
int len = nums.size();
int res = 0,k = 0;
sort(nums.begin(),nums.end());
for(int i = 0;i<len;++i){
k = i;
for(int j = i+1;j<len;++j){
while(k+1<len && nums[i]+nums[j]>nums[k+1]){//对k+1判断
++k;
}
res += max(k-j,0);
}
}
return res;
}
};
- 学习
查找->二分查找
双指针变化:对于需要三个标志
- 删除有序数组中的重复项 II 自己做出来了
- 思路
因为给定数组是有序的,所以相同元素必然连续。可以使用双指针解决本题,遍历数组检查每一个元素是否应该被保留,如果应该被保留,就将其移动到指定位置。
我的做法:在原数组中设置标志位,相同元素长度大于2就不往候选数组中添加
更聪明的做法:直接比较nums[fast]与nums[slow-2]是否相等 - 代码
我的做法;
int removeDuplicates(vector<int>& nums) {
int len = nums.size();
int idx = 1,pos = 1;
int flag = 0;
while(pos<len){
if(nums[pos] == nums[pos-1]) ++flag;
else flag = 0;
if(flag<2){
nums[idx++] = nums[pos++];
}else{
++pos;
}
}
return idx;
}
聪明的做法:
int removeDuplicates(vector<int>& nums) {
int len = nums.size();
if(len<2) return len;
int idx = 2,pos = 2;
while(pos<len){
if(nums[idx-2] != nums[pos]){
nums[idx] = nums[pos];
++idx;
}
++pos;
}
return idx;
}
- 学习
1:总结双指针使用的场景:
- 有序数组
2:原地算法,将当前元素添加到候选数组的条件判断:
先考虑在原数组中对元素处理是否能得出是否添加的结论