在数组中,所谓双指针就是两个变量,不一定真的是指针,双指针在处理数组,字符串场景下很常见,例子:从下面序列中删除重复元素,[1,2,2,2,3,3,3,3,5,5,7,8],重复元素只保留一个,删除后结果:[1,2,3,5,7,8],如图:
上面的一个在前一个在后的方式称为快慢指针,有些场景从两端向中间走,这种称为对撞型指针,还有背向型,从中间往两端走。
LeetCode27.给你一个数组和一个值val。你需要原地移除所有的数值等于val的元素,并返回移除后数组的新长度,要求:不要使用额外的数组空间,你必须仅使用O(1),额外空间并原地修改输入数组,元素的顺序可以改变,你不需要考虑数组中超出新长度后面的数组。
定义两个指针slow和fast,初始值都是0,slow之前是有效部分,fast表示当前要访问的元素,
1. 如果nums[fast] != val时候,将其移动到nums[slow++]处,slow同步移动
2. 如果nums[fast] = val时候,slow不动,fast向后移动图示:
代码:
public static int removeElement(int[] nums,int val){
int slow = 0;
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != val ){
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}
创建左右指针,分别在头尾处,
1. 当左边不等于val时候继续向右移动,如果等于val,和右指针不等于val时候,则替换,让右指针位置元素替换左指针,
2. 右指针不等于val时候停止移动,等待替换,等于val时候向前移动
3.直到左右碰撞的时候两指针停止。
以nums= [0,1,2,2,3,0,4,2] ,val = 2为例。
代码:
public static int remove2(int[] nums,int val){
int right = nums.length-1;
int left = 0;
for(left = 0; left <= right;){
if (nums[left] == val && nums[right] != val){
int temp = nums[left];
nums[left] = nums[right];
nums[right] = nums[temp];
}
}
while (nums[left] != val) left++;
while (nums[right] == val) right--;
return left;
}
2.2删除有序数组的重复项
不考虑数组中超出新长度后面的元素,
1. 每个元素只出现一次,不考虑只有一个元素的移动情况
2. slow指针指向第二个元素,让nums[fast]和nums[slow -1] 比较,相等则fast移动,不相等则将nums[fast]填充到nums[slow]处,slow++;fast++;
3.因为slow最后指向位置的元素是不需要的,所以数组长度就是slow
代码:
public static int remove(int[] nums){
//slow表示可以放入新元素的位置,索引为0的元素可不用管,
int slow = 1;
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != nums[slow -1]){
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}
接下来由于作者精力有限只为大家提供题目和思路,如果需要进一步学习,可在评论区留言或自信我。后续有空会为大家补上。
这个题目和2.1中的对撞双指针解题思路类似
1. 定义左右指针,在头部和尾部,左指针为偶数则向右移动,不为偶数和右指针为偶数同时满足时候,交换位置,
2.交换完继续,左指针为偶数向右移动,右指针为奇数向左移动,
3.返回值,最后交换完毕返回的数组,不用考虑边界问题
解题思路:
1. 首先对整个数组反转,结果[7,6,5,4,3,2,1]
2. 在K处将数组分为两组,[7,6,5], [4,3,2,1]
3. 再将两个数组都反转得到[5,6,7] 和[1,2,3,4]
解题思路:用快慢指针解题
1. 慢指针在每个区间的起始位置,快指针从慢指针处开始向后遍历,出现nums[fast]+1 != nums[fast+1]的时候达到边界此时[slow,fast]数组为一个区间
2.之后让slow=fast +1,重新开始遍历循环。
代码:
public static List ranges(int[] numns){
List res = new ArrayList<>();
//slow指向第一个区间的起始位置
int slow = 0;
//fast向后遍历直到不满足连续递增为止numns[fast] +1 != numns[fast +1]
//或者fast达到边界,则当前连续区间[slow,fast]遍历完毕,将其写入结果咧
for (int fast = 0; fast < numns.length; fast++) {
if (fast + 1 == numns.length && numns[fast] +1 != numns[fast +1]){
//将当前区间[slow,fast]写入结果列表
StringBuilder sb = new StringBuilder();
sb.append(numns[slow]);
if (slow != fast){
sb.append("->").append(numns[fast]);
}
res.add(sb.toString());
//将slow更新到下个区间起始位置
slow = fast +1;
}
}
return res;
}
解题思路:
第一种:当空间足够大的时候,将原始数组出现空格的位置直接替换成20%,
第二种:
1.先遍历一次数组,计算出字符串中的空格数,由此计算出替换后之后的字符串长度,每一个空格长度增加2,
2.从尾部开始复制和替换,用fast和slow指针分别指向原始字符串和新字符串的末尾,然后slow不动,fast移动,
3.若指向的不是空格,则复制到slow位置,然后两者同时移动一步,
4.若指向的是空格,在slow中插入20%,每插入一次移动一步,fast只移动一步,
代码:
public String replase(StringBuffer str){
if (str == null){
return null;
}
//空格数量
int numblank = 0;
int len = str.length();
for (int i = 0; i < len; i++) {
if (str.charAt(i) == ' '){
numblank++;
}
}
//计算总长度并设置长度
str.setLength(len + 2 * numblank);
//两个指针‘
int fast = len -1;
int slow = len + 2 * numblank -1;
//第二个条件
while (fast >= 0 && slow > fast){
char c = str.charAt(fast);
if (c == ' '){
fast--;
str.setCharAt(slow--,'0');
str.setCharAt(slow--,'2');
str.setCharAt(slow--,'%');
}else {
str.setCharAt(slow,c);
slow--;
fast--;
}
}
return str.toString();
}