1. 验证回文串
方法:我们直接在原字符串 s 上使用双指针。在移动任意一个指针时,需要不断地向另一指针的方向移动,直到遇到一个字母或数字字符,或者两指针重合为止。也就是说,我们每次将指针移到下一个字母字符或数字字符,再判断这两个指针指向的字符是否相同。
几个API: islower(char c) 是否为小写字母
isupper(char c) 是否为大写字母
isdigit(char c) 是否为数字
isalpha(char c) 是否为字母
isalnum(char c) 是否为字母或者数字
toupper(char c) 字母小转大
tolower(char c) 字母大转小
class Solution {
public:
bool isPalindrome(string s) {
int left=0,right=s.size()-1;
while(left <= right){
while(left<=right && !isalnum(s[left])){
left++;
}
while(left<=right && !isalnum(s[right])){
right--;
}
if(left<=right){ //防止出现空串
if(tolower(s[left])!=tolower(s[right])) return false;
}
left++;
right--;
}
return true;
}
};
2. 颜色分类
方法:我们用三个指针(p0, p2 和cur)来分别追踪0的最右边界,2的最左边界和当前考虑的元素
沿着数组移动 cur
指针,若 nums[cur] = 0
,则将其与 nums[p0]
互换,并将指针都向右移;若 nums[curr] = 2
,则与 nums[p2]
互换,并将 p2
指针左移。
Q: 为什么与p2交换,cur不右移?
A:curr 左边全都是0/1的有序序列!
- curr位置是0时,与左边的0位置交换,因为保证curr左边全是0/1, 所以交换过来的必然是0/1,状态维持住了;
- curr位置是2时,交换后,curr不能移动,因为一移动,没法保证交换过来的是0/1;所以这里不移动;这时状态也维持住了
class Solution {
public:
void sortColors(vector<int>& nums) {
int p0=0,cur=0,p2=nums.size()-1;
while(cur<=p2){
if(nums[cur]==0){
swap(nums[cur],nums[p0]);
cur++;
p0++;
}
else if(nums[cur]==2){
swap(nums[cur],nums[p2]);
p2--;
}
else cur++;
}
}
};
3. 划分字母区间
分析:从第一个字母开始分析,假设第一个字母是 'a',那么第一个区间一定包含最后一次出现的 'a'。但第一个出现的 'a' 和最后一个出现的 'a' 之间可能还有其他字母,这些字母会让区间变大。举个例子,在 "abccaddbeffe" 字符串中,第一个最小的区间是 "abccaddb"
方法:定义数组 last[char] 来表示字符 char 最后一次出现的下标。定义 anchor 和 j 来表示当前区间的首尾。如果遇到的字符最后一次出现的位置下标大于 j, 就让 j=last[c] 来拓展当前的区间。当遍历到了当前区间的末尾时(即 i==j ),把当前区间加入答案,同时将 ancor 设为 i+1 去找下一个区间。
class Solution {
public:
vector<int> partitionLabels(string S) {
vector<int> last(26);
for(int i=0; i<S.size(); i++){
last[S[i]-'a']=i;
}
vector<int> res;
int ancor=0, j=0;
for(int i=0; i<S.size(); i++){
j=max(j,last[S[i]-'a']); //拓展当前的区间
if(i==j){
res.push_back(i-ancor+1);
ancor=i+1; //更新ancor
}
}
return res;
}
};
4. 合并两个有序数组
从前往后:最直接的算法实现是将指针p1 置为 nums1的开头, p2为 nums2的开头,在每一步将最小值放入输出数组中。由于 nums1 是用于输出的数组,需要将nums1中的前m个元素放在其他地方(复制一份数组和nums2比较,再nums1上修改),也就需要 O(m)的空间复杂度。
从后往前:如果我们从结尾开始改写 nums1
的值,这里没有信息,因此不需要额外空间。
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int i=m+n-1; //注意i的取值!!
int p1=m-1,p2=n-1;
while(p1>=0 && p2>=0){
if(nums1[p1]<nums2[p2]){
nums1[i--]=nums2[p2--];
}
else{
nums1[i--]=nums1[p1--];
}
}
while(p2>=0) nums1[i--]=nums2[p2--]; //nums1合并完,nums2还未合并完
}
};
5. 三数之和
方法:排序+双指针
1. 特判,对于数组长度 n,如果数组为 null 或者数组长度小于 3,返回 {}。
2. 对数组进行排序。
3. 遍历排序后数组:笃定三指针的最小数字的指针i,双指针L,R分别设在(i,len(nums))两端。
(1)若 nums[i]>0:因为已经排序好,所以后面不可能有三个数加和等于0,直接返回结果。
(2)对于重复元素:跳过,避免出现重复解 nums[k]==nums[k-1] continue;
(3)令左指针 L=i+1,右指针 R=n-1,当 L<R时,执行循环:
当 nums[i]+nums[L]+nums[R]==0,并同时将 L,R移到下一位置,寻找新的解, 执行循环,
判断左界和右界是否和下一位置重复,去除重复解。nums[L]==nums[L-1] nums[R]==nums[R-1]
若和大于 0,说明 nums[R] 太大,R 左移
若和小于 0,说明 nums[L] 太小,L 右移
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
if(nums.size()<3) return {};
sort(nums.begin(),nums.end()); //排序
for(int k=0; k+2<nums.size(); k++){
if(nums[k]>0) return res;
if(k>0 && nums[k]==nums[k-1]) continue;
int target = -nums[k];
int l=k+1,r=nums.size()-1;
while(l<r){
if(nums[l]+nums[r]==target){
res.push_back({nums[k],nums[l],nums[r]});
l++;
r--;
while(l<r && nums[l]==nums[l-1]) l++;
while(l<r && nums[r]==nums[r+1]) r--;
}
else if(nums[l]+nums[r]>target){
r--;
}
else{
l++;
}
}
}
return res;
}
};
6.四数之和
思路:和三数之和一个道理。
使用四个指针。固定最小的 i 和 j 在左边,l=i+1, r=n-1 移动两个指针包夹求解。
保存使得nums[i]+nums[j]+nums[l]+nums[r]==target的解。偏大时 r 左移,偏小时 l 右移。c和d相遇时,表示以当前的 i 和j j 为最小值的解已经全部求得。
j++,进入下一轮循环b循环,当 j 循环结束后。 i++,进入下一轮 i 循环。 即(i 在最外层循环,里面嵌套 j 循环,再嵌套双指针l,r包夹求解)。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> res;
sort(nums.begin(),nums.end());
int n=nums.size();
if(n<4) return res;
for(int i=0; i+3<n; i++){
if(i>0 && nums[i]==nums[i-1]) continue;
for(int j=i+1; j+2<n; j++){
if(j>i+1 && nums[j]==nums[j-1]) continue;
int target1=target-nums[i]-nums[j];
int l=j+1, r=n-1;
while(l<r){
if(nums[l]+nums[r]==target1){
res.push_back(vector<int>{nums[i], nums[j], nums[l], nums[r]});
l++;
r--;
while(l<r && nums[l]==nums[l-1]) l++;
while(l<r && nums[r]==nums[r+1]) r--;
}
else if(nums[l]+nums[r]>target1){
r--;
}
else{
l++;
}
}
}
}
return res;
}
};