LeetCode之——双指针
双指针
双指针有的又叫快慢指针,有的也分细为快慢指针和左右指针,主要用来解决数组、字符串、链表等问题。今天的题涉及的比较浅,主要是解决简单的数组和字符串的问题。
题1:leetcode27. 移除元素
给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例1:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
示例2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
提示:
- 0 <= nums.length <= 100
- 0 <= nums[i] <= 50
- 0 <= val <= 100
解题思路:
题干中强调了“原地”,因此我们不能重新新建另一个数组来解题。这道题我们采用快慢指针,先来理解一下快慢指针的过程:
令 slowIdx(慢指针) 指向下标 0,fastIdx(快指针) 也指向下标0, fastIdx往前移动,如果对应的数值与所删除的元素不等,则把当前数值赋值给slowIdx对应位置的值。fastIdx 其实就是 for 循环中自增的 i, 遍历一遍后 slowIdx+1 就是我们所需的长度。具体过程leetcode的官方题解以及评论区有视频和图解,不理解的可以去好好看看。
接下来上代码:
public static void main(String[] args) {
int[] nums = new int[]{0,1,2,2,3,0,4,2};
int val = 2;
int len = removeElement(nums, val);
for (int i = 0; i < len; i++) {
System.out.print(nums[i]+" ");
}
}
// 区区几行代码就不多作注解了
private static int removeElement(int[] nums, int val) {
int slowIdx = 0;
for (int fastIdx = 0; fastIdx < nums.length; fastIdx++) {
if (nums[fastIdx] != val){
nums[slowIdx] = nums[fastIdx];
slowIdx++;
}
}
return slowIdx;
与上题类似的还有leetcode26. 删除有序数组中的重复项以及leetcode283. 移动0,这里就不做具体的分析,直接给大家上代码吧(懒了):
leetcode26:
public static void main(String[] args) {
int[] nums = new int[]{};
int len = removeDuplicates(nums);
for (int i = 0; i < len; i++) {
System.out.print(nums[i]+" ");
}
}
private static int removeDuplicates(int[] nums) {
if (nums.length == 0)
return 0; //leetcode里return -1 会出错;
// 这里slowIdx从1开始是因为一开始将第一个数作为双指针中的val值
int slowIdx = 1;
int val = nums[0];
for (int fastIdx = 0; fastIdx < nums.length; fastIdx++) {
if (nums[fastIdx] != val){
val = nums[fastIdx];
nums[slowIdx++] = val;
}
}
return slowIdx;
}
leetcode283:
public static void main(String[] args) {
int[] nums = new int[]{0,1,0,3,12};
moveZeroes(nums);
for (int i = 0; i < nums.length; i++) {
System.out.print(nums[i]+" ");
}
}
// 思路就是想将0作为移除的val,计算有多少个0,然后最后将这些0插入到数组后面
private static void moveZeroes(int[] nums) {
int val = 0;
int slowIdx = 0;
int zeroNum = 0;
for (int fastIdx = 0; fastIdx < nums.length; fastIdx++) {
if (nums[fastIdx] != val){
nums[slowIdx++] = nums[fastIdx];
}
else
zeroNum++;
}
// 往后插入0
for (int i = nums.length - zeroNum; i
题2:leetcode844. 比较含退格的字符串
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,请你判断二者是否相等。# 代表退格字符。
如果相等,返回 true ;否则,返回 false 。
注意:如果对空文本输入退格字符,文本继续为空
示例1:
输入:s = "ab#c", t = "ad#c"
输出:true
解释:S 和 T 都会变成 “ac”。
示例2:
输入:s = "ab##", t = "c#d#"
输出:true
解释:s 和 t 都会变成 “”。
示例3:
输入:s = "a##c", t = "#a#c"
输出:true
解释:s 和 t 都会变成 “c”。
示例4:
输入:s = "a#c", t = "b"
输出:false
解释:s 会变成 “c”,但 t 仍然是 “b”。
提示:
- 1 <= s.length, t.length <= 200
- s 和 t 只含有小写字母以及字符 '#'
解题思路:
这道题相信很多人不会立刻想到双指针(没错就是我自己),一般都是将字符串中该删除的删除,对重构后的两个字符串进比较。不过它标签之一既然有双指针,那就是能用双指针来求解。看了官方的双指针求解,采用逆序遍历比较的方法,可怎么感觉比一般双指针麻烦了很多,当然官方还有动图解释,大家可以去看看。
这道题其实不用官方那么麻烦,跟之前的套路一样,当然我们首先是将字符串变为数组,然后就是将'#'作为val值,如果fastIdx搜寻到val,那么按照题目意思需要删除前一个数据,那么只需要把slowIdx-1就可以了。最后就可以得到删除后的字符串片段(竟然是利用双指针来重构字符串,有被惊到),话不多说,上代码:
public static void main(String[] args) {
String s = "a#c", t = "b";
boolean out = backspaceCompare(s, t);
System.out.println(out);
}
private static boolean backspaceCompare(String s, String t) {
// 双指针最好是对数组进行操作,因此这里将字符串转为数组
String ss = getStr(s.toCharArray());
String tt = getStr(t.toCharArray());
return ss.equals(tt);
}
private static String getStr(char[] str) {
// 使用时slowIdx先+1,再赋值,便于else if里-1
int slowIdx = -1;
char space = '#';
for (int fastIdx = 0; fastIdx < str.length; fastIdx++) {
if (str[fastIdx] != space){
slowIdx++;
str[slowIdx] = str[fastIdx];
}
else if (slowIdx >= 0){
slowIdx--;
}
}
// copyOfRange方法截取数组
char[] result = Arrays.copyOfRange(str,0,slowIdx+1);
// 返回值转为String
return new String(result);
}
题3:leetcode977. 有序数组的平方
给你一个按非递减顺序 排序的整数数组 nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。
示例1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
示例2:
输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]
提示:
- $1 <= nums.length <= 10^4$
- $-10^4 <= nums[i] <= 10^4$
- nums 以按非递减顺序排序
解题思路:
这道题其实很简单,数组中逐个平方后再排序就行,当然,排序方法的不同效率也不同,也可以直接调用java自带的Arrays.sort()方法,它用了一种名为TimSort的排序算法,是归并排序的优化版本。而官方有一个解就是双指针+归并排序的,有兴趣的可以去学习。
这里不直接调用java自带的排序, 根据题目可以知道,数组里如果存在负数,平方后数值是先减少后增大的(数组非递减),故我们可以直接比较首尾数值平方后的大小,然后利用首尾双指针将最大值插入最后一位,下面看代码:
public static void main(String[] args) {
int[] nums = new int[]{-7,-3,2,3,11};
int[] result = sortedSquares(nums);
for (int i = 0; i < result.length; i++) {
System.out.print(result[i]+" ");
}
}
private static int[] sortedSquares(int[] nums) {
// 新建长度与nums一样的数组,作为返回数组
int[] res = new int[nums.length];
// 初始化头尾指针
int leftIdx = 0, rightIdx = nums.length-1;
int idx = nums.length-1; //这是插入值的顺序
while (leftIdx <= rightIdx){
// 比较首尾平方的大小,然后更新对应的首尾指针
if (nums[leftIdx] * nums[leftIdx] >= nums[rightIdx] * nums[rightIdx]){
res[idx--] = nums[leftIdx] * nums[leftIdx];
leftIdx++;
}
else{
res[idx--] = nums[rightIdx] * nums[rightIdx];
rightIdx--;
}
}
return res;
}
刷题参考路线:代码随想录(微信公众号)