#目的:删除数组中的重复元素/目标值为target的元素
题例:
虽然用穷举法可以达到目的,但要花费O(n^2)的时间开销,这里提出一种双指针法,原地地达成目标
双指针法:
使用一个快指针和一个慢指针遍历数组
快指针的职责:往后遍历新数组元素,相当于工作指针
慢指针的职责:原地更新数组的数据构成,帮助快指针实现数组重复元素覆盖的工作
code:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int fast=0;
int slow=0;
for( fast ; fast<nums.size(); fast++)
{
if(nums[fast] != nums[slow])
{
nums[++slow]=nums[fast];
}
}
//cout<<fast-1;
for(int i=0;i<=slow;i++)
{
cout<<nums[i];
}
return slow+1;
}
};
扩展题1:移动零
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
这题如果记录0元素的位置,并挪到数组末尾,时间开销较大,故我们换个思路,记录非0的元素的位置,快指针指向非0元素时,将其挪到慢指针下标处(从慢指针开始原地覆盖原数组),最后,我们用0填充慢指针到快指针之间的位置即可。
code:
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int fast = 0;
int slow = 0;
for (fast; fast < nums.size(); fast++)
{
if (nums[fast] != 0)
nums[slow++] = nums[fast];
}
for (int j = slow; j < nums.size(); j++)
nums[j] = 0;
// for(int i=0;i<nums.size();i++)
// cout << nums[i];
}
};
扩展题2:有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
暴力解法:
先每个元素平方后再进行排序
时间复杂度: 空间复杂度:
双指针法:
方法1:由于数组整体是非递减顺序,那么平方后最大的数就在数组的的两端,且越靠近数组中央。其平方后的值越小。故我们使用两个指针start和end,从数组的两端向中间遍历,取平方的较大值填入辅助数组中。
方法2:我们先找到负数与正数的分界线,得到了分界线后,分界线两边的元素离得越远,其绝对值越大。故我们从分界线开始,负数部分一个指针,正数部分一个指针,每次比较两个指针指的数的绝对值大小,依次填入新数组中。
code:
方法一:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int len=nums.size();
vector<int> sortnum(len);
int start=0;
int end=nums.size()-1;
int j=len-1;
while(start <= end)
{
if(nums[start]*nums[start] <= nums[end]*nums[end])
{
sortnum[j--] = nums[end]*nums[end];
end--;
}
else
{
sortnum[j--] = nums[start]*nums[start];
start++;
}
}
return sortnum;
}
};
扩展题3:比较含退格的字符串
给定 s
和 t
两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true
。#
代表退格字符。
注意:如果对空文本输入退格字符,文本继续为空
方法1:重构字符串
我们使用辅助数组来装载 经过删除处理的字符串,最后将两个处理过的字符串S、T进行比较
代码:
class Solution {
public:
bool backspaceCompare(string s, string t) {
return backspace(s) == backspace(t);
}
string backspace(string m)
{
string str = "";
for(char i:m)
{
if(i != '#')
str.push_back(i);
else if(!m.empty())//防止栈里已空还要求弹出
str.pop_back();
}
return str;
}
};
方法2:双指针
一个字符是否会被删掉,只取决于该字符后面的退格符,而与该字符前面的退格符无关。因此当我们逆序地遍历字符串,就可以立即确定当前字符是否会被删掉。
具体地,我们定义skip表示当前待删除的字符的数量。每次我们遍历到一个字符:
若该字符为退格符,则我们需要多删除一个普通字符,我们让skip加1;
若该字符为普通字符:
若skip为0,则说明当前字符不需要删去;
若skip不为0,则说明当前字符需要删去,我们让skip 减 1。
这样,我们定义两个指针,分别指向两字符串的末尾。每次我们让两指针逆序地遍历两字符串,直到两字符串能够各自确定一个字符,然后将这两个字符进行比较。重复这一过程直到找到的两个字符不相等,或遍历完字符串为止。
作者:力扣官方题解
扩展题4:找出最长的神奇数列
小F是一个好学的中学生,今天他学习了数列的概念。他在纸上写下了一个由 0
和 1
组成的正整数序列,长度为 n
。这个序列中的 1
和 0
交替出现,且至少由 3 个连续的 0
和 1
组成的部分数列称为「神奇数列」。例如,10101
是一个神奇数列,而 1011
不是。现在,小F想知道在这个序列中,最长的「神奇数列」是哪一个。你能帮他找到吗?
方法1:暴力法
方法2:双指针法:
双指针法的直观想法很简单,2个指针分别指向嫌疑神奇序列的左端和右端,右端指针如果察觉到右边的数字仍然满足条件(数字交替),则继续往右边扩展。如果不满足条件,左右指针会进行更新,寻找新的嫌疑神奇序列。
由于题目涉及到“最大”,所以搜索期间需要用maxlen和一个maxstring来记录当前最长的神奇序列。
需要注意的是当j遍历到最后一个元素时,maxlen和maxstring还没来得及保存就跳出循环了,所以需要在while循环结束时再检查一下。
public static String solution(String inp) {
// Edit your code here
if(inp.length()<2)
return "";
int i=0;
int j=i+1;
//遍历一遍
int mlen=Integer.MIN_VALUE;
String res="";
int len=1;
while(j<inp.length())
{
if(inp.charAt(j) != inp.charAt(j-1))
{
len++;
j++;
}
else
{
//先保存现有结果
if(mlen<len && len>=3)
{
mlen=len;
res=inp.substring(i, j);
}
//重新开始
len=1;
i=j;
j=i+1;
}
}
//处理最后一个元素
if(mlen < len&&len>=3) {
mlen = len;
res = inp.substring(i, j);
}
return res;
}