双指针常见用法
一、双指针法将时间复杂度O(n^2)的解法优化为 O(n)的解法,可以将时间复杂度一个数量级。
- 27 移除元素
- 15 三数之和
- 18 四数之和
二、 双指针来记录前后指针实现链表反转
- 206 反转链表
三、 使用双指针来确定有环
- 142 环形链表2
LeetCode26 删除排序数组中的重复项
题目
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
双指针思路:
定义两个指针,快指针和慢指针
快指针用于发现重复元素,慢指针用于更新数组
扫描二维码关注公众号,回复:
13136065 查看本文章
-
当发现重复元素时,快指针跳过,慢指针不动
-
当确认快指针指向的元素与下一个元素不相同时,更新快慢指针,同时更新数组
代码
/**
* @version V1.0
* @ClassName:Leet26
* @Description: 删除排序数组中的重复项
* @author:Daniel
* @date:2021/1/24 上午10:53
*/
public class Leet26 {
public static void main(String[] args) {
int[] nums = {
0,0,1,1,1,2,2,3,3,4};
int i = removeDuplicates(nums);
System.out.println(i);
}
// 法一:快慢指针
public static int removeDuplicates(int[] nums) {
int i = 0; // 慢指针
for (int j = 0; j < nums.length - 1; j ++) {
// 快指针
if (nums[j+1] == nums[j]) {
continue;
}
nums[++i] = nums[j+1]; // 利用第一个元素一定不是重复的,进行错位
}
return i+1;
}
}
LeetCode27 移除元素
题目
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-element
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
两种暴力解法的不同思路,并对第一种暴力解法做了优化。
使用快慢指针。
代码
/**
* @version V1.0
* @ClassName:Leet26
* @Description: 移除元素
* @author:Daniel
* @date:2021/1/23 下午5:00
*/
public class Leet27 {
public static void main(String[] args) {
int[] nums = {
3,2,2,3};
int val = 2;
int i = removeElement4(nums, 2);
System.out.println(i);
}
// nums = [0,1,2,2,3,0,4,2], val = 2
// 返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
// 法一:暴力解法
public static int removeElement(int[] nums, int val) {
// 记录新数组长度
int size = nums.length;
for (int i = 0; i < size; ) {
if (nums[i] == val) {
size--;
// 覆盖
for (int j = i; j < nums.length - 1; j++) {
nums[j] = nums[j + 1];
}
}
// 覆盖后,需要继续判断下标i处的元素是否等于val,若等则继续在i处覆盖,不等则i++
i = (nums[i] == val) ? i : i + 1;
}
return size;
}
// 对法一的优化
public static int removeElement4(int[] nums, int val) {
int size = nums.length;
for (int i = 0; i < size; i++) {
if (nums[i] == val) {
size --;
for (int j = i; j < nums.length - 1; j ++) {
nums[j] = nums[j+1];
}
i--; // 优化,i也前移一位,重新判断nums[i]是否等于val
}
}
return size;
}
// 法二:暴力解法 (别人提供的思路,比较优美)
public static int removeElement2(int[] nums, int val) {
// 新数组长度
int size = nums.length;
for (int i = 0; i < size; i++) {
if (nums[i] == val) {
for (int j = i+1; j < size; j++) {
nums[j-1] = nums[j];
}
i --; // 因为下标i以后的数值都向前移动一位了,所以i也向前移动一位
size --; // 此时数组的大小-1
}
}
return size;
}
// 解法三:快慢指针
// 思路:
// 1. 两个指针,前面一个后面一个,前面的用于搜索需要删除的值。
// 2. 当遇到需要删除的值时,前指针直接跳过,后面的指针不动。
// 3. 当遇到正常值时,两个指针都进行移动,并修改慢指针的值。
public static int removeElement3(int[] nums, int val) {
int i = 0; // 慢指针
for (int j = 0 ; j < nums.length; j++) {
// 快指针
if (nums[j] == val) {
// 如果等于目标值,则跳过(删除)
continue;
} else {
// 如果不等于目标值,则赋值给 num[i], 并更新慢指针
nums[i++] = nums[j];
}
}
return i;
}
}